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; // = bb_msg_standard_input;
 623        /* The width of the screen, for output wrapping */
 624        unsigned max_col; // = 80;
 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 set_window_size(const int rows, const int cols)
 738{
 739        struct winsize win = { 0, 0, 0, 0 };
 740
 741        if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
 742                if (errno != EINVAL) {
 743                        goto bail;
 744                }
 745                memset(&win, 0, sizeof(win));
 746        }
 747
 748        if (rows >= 0)
 749                win.ws_row = rows;
 750        if (cols >= 0)
 751                win.ws_col = cols;
 752
 753        if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
 754bail:
 755                perror_on_device("%s");
 756}
 757
 758static void display_window_size(const int fancy)
 759{
 760        const char *fmt_str = "%s\0%s: no size information for this device";
 761        unsigned width, height;
 762
 763        if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
 764                if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
 765                        perror_on_device(fmt_str);
 766                }
 767        } else {
 768                wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
 769                                height, width);
 770        }
 771}
 772
 773static const struct suffix_mult stty_suffixes[] = {
 774        { "b",  512 },
 775        { "k", 1024 },
 776        { "B", 1024 },
 777        { }
 778};
 779
 780static const struct mode_info *find_mode(const char *name)
 781{
 782        int i = index_in_strings(mode_name, name);
 783        return i >= 0 ? &mode_info[i] : NULL;
 784}
 785
 786static const struct control_info *find_control(const char *name)
 787{
 788        int i = index_in_strings(control_name, name);
 789        return i >= 0 ? &control_info[i] : NULL;
 790}
 791
 792enum {
 793        param_need_arg = 0x80,
 794        param_line    = 1 | 0x80,
 795        param_rows    = 2 | 0x80,
 796        param_cols    = 3 | 0x80,
 797        param_columns = 4 | 0x80,
 798        param_size    = 5,
 799        param_speed   = 6,
 800        param_ispeed  = 7 | 0x80,
 801        param_ospeed  = 8 | 0x80,
 802};
 803
 804static int find_param(const char *name)
 805{
 806        static const char params[] ALIGN1 =
 807                "line\0"    /* 1 */
 808                "rows\0"    /* 2 */
 809                "cols\0"    /* 3 */
 810                "columns\0" /* 4 */
 811                "size\0"    /* 5 */
 812                "speed\0"   /* 6 */
 813                "ispeed\0"
 814                "ospeed\0";
 815        int i = index_in_strings(params, name) + 1;
 816        if (i == 0)
 817                return 0;
 818        if (i != 5 && i != 6)
 819                i |= 0x80;
 820        return i;
 821}
 822
 823static int recover_mode(const char *arg, struct termios *mode)
 824{
 825        int i, n;
 826        unsigned chr;
 827        unsigned long iflag, oflag, cflag, lflag;
 828
 829        /* Scan into temporaries since it is too much trouble to figure out
 830           the right format for 'tcflag_t' */
 831        if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
 832                           &iflag, &oflag, &cflag, &lflag, &n) != 4)
 833                return 0;
 834        mode->c_iflag = iflag;
 835        mode->c_oflag = oflag;
 836        mode->c_cflag = cflag;
 837        mode->c_lflag = lflag;
 838        arg += n;
 839        for (i = 0; i < NCCS; ++i) {
 840                if (sscanf(arg, ":%x%n", &chr, &n) != 1)
 841                        return 0;
 842                mode->c_cc[i] = chr;
 843                arg += n;
 844        }
 845
 846        /* Fail if there are too many fields */
 847        if (*arg != '\0')
 848                return 0;
 849
 850        return 1;
 851}
 852
 853static void display_recoverable(const struct termios *mode,
 854                                int UNUSED_PARAM dummy)
 855{
 856        int i;
 857        printf("%lx:%lx:%lx:%lx",
 858                   (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
 859                   (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
 860        for (i = 0; i < NCCS; ++i)
 861                printf(":%x", (unsigned int) mode->c_cc[i]);
 862        bb_putchar('\n');
 863}
 864
 865static void display_speed(const struct termios *mode, int fancy)
 866{
 867                             //01234567 8 9
 868        const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
 869        unsigned long ispeed, ospeed;
 870
 871        ospeed = ispeed = cfgetispeed(mode);
 872        if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
 873                ispeed = ospeed;                /* in case ispeed was 0 */
 874                         //0123 4 5 6 7 8 9
 875                fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
 876        }
 877        if (fancy) fmt_str += 9;
 878        wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
 879}
 880
 881static void do_display(const struct termios *mode, const int all)
 882{
 883        int i;
 884        tcflag_t *bitsp;
 885        unsigned long mask;
 886        int prev_type = control;
 887
 888        display_speed(mode, 1);
 889        if (all)
 890                display_window_size(1);
 891#ifdef HAVE_C_LINE
 892        wrapf("line = %d;\n", mode->c_line);
 893#else
 894        wrapf("\n");
 895#endif
 896
 897        for (i = 0; i != CIDX_min; ++i) {
 898                /* If swtch is the same as susp, don't print both */
 899#if VSWTCH == VSUSP
 900                if (i == CIDX_swtch)
 901                        continue;
 902#endif
 903                /* If eof uses the same slot as min, only print whichever applies */
 904#if VEOF == VMIN
 905                if ((mode->c_lflag & ICANON) == 0
 906                 && (i == CIDX_eof || i == CIDX_eol)
 907                ) {
 908                        continue;
 909                }
 910#endif
 911                wrapf("%s = %s;", nth_string(control_name, i),
 912                          visible(mode->c_cc[control_info[i].offset]));
 913        }
 914#if VEOF == VMIN
 915        if ((mode->c_lflag & ICANON) == 0)
 916#endif
 917                wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
 918        if (G.current_col) wrapf("\n");
 919
 920        for (i = 0; i < NUM_mode_info; ++i) {
 921                if (mode_info[i].flags & OMIT)
 922                        continue;
 923                if (mode_info[i].type != prev_type) {
 924                        /* wrapf("\n"); */
 925                        if (G.current_col) wrapf("\n");
 926                        prev_type = mode_info[i].type;
 927                }
 928
 929                bitsp = mode_type_flag(mode_info[i].type, mode);
 930                mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
 931                if ((*bitsp & mask) == mode_info[i].bits) {
 932                        if (all || (mode_info[i].flags & SANE_UNSET))
 933                                wrapf("-%s"+1, nth_string(mode_name, i));
 934                } else {
 935                        if ((all && mode_info[i].flags & REV)
 936                         || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
 937                        ) {
 938                                wrapf("-%s", nth_string(mode_name, i));
 939                        }
 940                }
 941        }
 942        if (G.current_col) wrapf("\n");
 943}
 944
 945static void sane_mode(struct termios *mode)
 946{
 947        int i;
 948        tcflag_t *bitsp;
 949
 950        for (i = 0; i < NUM_control_info; ++i) {
 951#if VMIN == VEOF
 952                if (i == CIDX_min)
 953                        break;
 954#endif
 955                mode->c_cc[control_info[i].offset] = control_info[i].saneval;
 956        }
 957
 958        for (i = 0; i < NUM_mode_info; ++i) {
 959                if (mode_info[i].flags & SANE_SET) {
 960                        bitsp = mode_type_flag(mode_info[i].type, mode);
 961                        *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
 962                                | mode_info[i].bits;
 963                } else if (mode_info[i].flags & SANE_UNSET) {
 964                        bitsp = mode_type_flag(mode_info[i].type, mode);
 965                        *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
 966                                & ~mode_info[i].bits;
 967                }
 968        }
 969}
 970
 971/* Save set_mode from #ifdef forest plague */
 972#ifndef ONLCR
 973#define ONLCR 0
 974#endif
 975#ifndef OCRNL
 976#define OCRNL 0
 977#endif
 978#ifndef ONLRET
 979#define ONLRET 0
 980#endif
 981#ifndef XCASE
 982#define XCASE 0
 983#endif
 984#ifndef IXANY
 985#define IXANY 0
 986#endif
 987#ifndef TABDLY
 988#define TABDLY 0
 989#endif
 990#ifndef OXTABS
 991#define OXTABS 0
 992#endif
 993#ifndef IUCLC
 994#define IUCLC 0
 995#endif
 996#ifndef OLCUC
 997#define OLCUC 0
 998#endif
 999#ifndef ECHOCTL
1000#define ECHOCTL 0
1001#endif
1002#ifndef ECHOKE
1003#define ECHOKE 0
1004#endif
1005
1006static void set_mode(const struct mode_info *info, int reversed,
1007                                        struct termios *mode)
1008{
1009        tcflag_t *bitsp;
1010
1011        bitsp = mode_type_flag(info->type, mode);
1012
1013        if (bitsp) {
1014                if (reversed)
1015                        *bitsp = *bitsp & ~info->mask & ~info->bits;
1016                else
1017                        *bitsp = (*bitsp & ~info->mask) | info->bits;
1018                return;
1019        }
1020
1021        /* Combination mode */
1022        if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
1023                if (reversed)
1024                        mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1025                else
1026                        mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
1027        } else if (info == &mode_info[IDX_oddp]) {
1028                if (reversed)
1029                        mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1030                else
1031                        mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
1032        } else if (info == &mode_info[IDX_nl]) {
1033                if (reversed) {
1034                        mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
1035                        mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
1036                } else {
1037                        mode->c_iflag = mode->c_iflag & ~ICRNL;
1038                        if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1039                }
1040        } else if (info == &mode_info[IDX_ek]) {
1041                mode->c_cc[VERASE] = CERASE;
1042                mode->c_cc[VKILL] = CKILL;
1043        } else if (info == &mode_info[IDX_sane]) {
1044                sane_mode(mode);
1045        } else if (info == &mode_info[IDX_cbreak]) {
1046                if (reversed)
1047                        mode->c_lflag |= ICANON;
1048                else
1049                        mode->c_lflag &= ~ICANON;
1050        } else if (info == &mode_info[IDX_pass8]) {
1051                if (reversed) {
1052                        mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1053                        mode->c_iflag |= ISTRIP;
1054                } else {
1055                        mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1056                        mode->c_iflag &= ~ISTRIP;
1057                }
1058        } else if (info == &mode_info[IDX_litout]) {
1059                if (reversed) {
1060                        mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1061                        mode->c_iflag |= ISTRIP;
1062                        mode->c_oflag |= OPOST;
1063                } else {
1064                        mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1065                        mode->c_iflag &= ~ISTRIP;
1066                        mode->c_oflag &= ~OPOST;
1067                }
1068        } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
1069                if ((info == &mode_info[IDX_raw] && reversed)
1070                 || (info == &mode_info[IDX_cooked] && !reversed)
1071                ) {
1072                        /* Cooked mode */
1073                        mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1074                        mode->c_oflag |= OPOST;
1075                        mode->c_lflag |= ISIG | ICANON;
1076#if VMIN == VEOF
1077                        mode->c_cc[VEOF] = CEOF;
1078#endif
1079#if VTIME == VEOL
1080                        mode->c_cc[VEOL] = CEOL;
1081#endif
1082                } else {
1083                        /* Raw mode */
1084                        mode->c_iflag = 0;
1085                        mode->c_oflag &= ~OPOST;
1086                        mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1087                        mode->c_cc[VMIN] = 1;
1088                        mode->c_cc[VTIME] = 0;
1089                }
1090        }
1091        else if (IXANY && info == &mode_info[IDX_decctlq]) {
1092                if (reversed)
1093                        mode->c_iflag |= IXANY;
1094                else
1095                        mode->c_iflag &= ~IXANY;
1096        }
1097        else if (TABDLY && info == &mode_info[IDX_tabs]) {
1098                if (reversed)
1099                        mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1100                else
1101                        mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1102        }
1103        else if (OXTABS && info == &mode_info[IDX_tabs]) {
1104                if (reversed)
1105                        mode->c_oflag |= OXTABS;
1106                else
1107                        mode->c_oflag &= ~OXTABS;
1108        } else
1109        if (XCASE && IUCLC && OLCUC
1110         && (info == &mode_info[IDX_lcase] || info == &mode_info[IDX_LCASE])
1111        ) {
1112                if (reversed) {
1113                        mode->c_lflag &= ~XCASE;
1114                        mode->c_iflag &= ~IUCLC;
1115                        mode->c_oflag &= ~OLCUC;
1116                } else {
1117                        mode->c_lflag |= XCASE;
1118                        mode->c_iflag |= IUCLC;
1119                        mode->c_oflag |= OLCUC;
1120                }
1121        } else if (info == &mode_info[IDX_crt]) {
1122                mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1123        } else if (info == &mode_info[IDX_dec]) {
1124                mode->c_cc[VINTR] = 3; /* ^C */
1125                mode->c_cc[VERASE] = 127; /* DEL */
1126                mode->c_cc[VKILL] = 21; /* ^U */
1127                mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1128                if (IXANY) mode->c_iflag &= ~IXANY;
1129        }
1130}
1131
1132static void set_control_char_or_die(const struct control_info *info,
1133                        const char *arg, struct termios *mode)
1134{
1135        unsigned char value;
1136
1137        if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
1138                value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1139        else if (arg[0] == '\0' || arg[1] == '\0')
1140                value = arg[0];
1141        else if (!strcmp(arg, "^-") || !strcmp(arg, "undef"))
1142                value = _POSIX_VDISABLE;
1143        else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1144                value = arg[1] & 0x1f; /* Non-letters get weird results */
1145                if (arg[1] == '?')
1146                        value = 127;
1147        } else
1148                value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1149        mode->c_cc[info->offset] = value;
1150}
1151
1152#define STTY_require_set_attr   (1 << 0)
1153#define STTY_speed_was_set      (1 << 1)
1154#define STTY_verbose_output     (1 << 2)
1155#define STTY_recoverable_output (1 << 3)
1156#define STTY_noargs             (1 << 4)
1157
1158int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1159int stty_main(int argc, char **argv)
1160{
1161        struct termios mode;
1162        void (*output_func)(const struct termios *, const int);
1163        const char *file_name = NULL;
1164        int display_all = 0;
1165        int stty_state;
1166        int k;
1167
1168        INIT_G();
1169
1170        stty_state = STTY_noargs;
1171        output_func = do_display;
1172
1173        /* First pass: only parse/verify command line params */
1174        k = 0;
1175        while (argv[++k]) {
1176                const struct mode_info *mp;
1177                const struct control_info *cp;
1178                const char *arg = argv[k];
1179                const char *argnext = argv[k+1];
1180                int param;
1181
1182                if (arg[0] == '-') {
1183                        int i;
1184                        mp = find_mode(arg+1);
1185                        if (mp) {
1186                                if (!(mp->flags & REV))
1187                                        goto invalid_argument;
1188                                stty_state &= ~STTY_noargs;
1189                                continue;
1190                        }
1191                        /* It is an option - parse it */
1192                        i = 0;
1193                        while (arg[++i]) {
1194                                switch (arg[i]) {
1195                                case 'a':
1196                                        stty_state |= STTY_verbose_output;
1197                                        output_func = do_display;
1198                                        display_all = 1;
1199                                        break;
1200                                case 'g':
1201                                        stty_state |= STTY_recoverable_output;
1202                                        output_func = display_recoverable;
1203                                        break;
1204                                case 'F':
1205                                        if (file_name)
1206                                                bb_error_msg_and_die("only one device may be specified");
1207                                        file_name = &arg[i+1]; /* "-Fdevice" ? */
1208                                        if (!file_name[0]) { /* nope, "-F device" */
1209                                                int p = k+1; /* argv[p] is argnext */
1210                                                file_name = argnext;
1211                                                if (!file_name)
1212                                                        bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1213                                                /* remove -F param from arg[vc] */
1214                                                --argc;
1215                                                while (argv[p]) { argv[p] = argv[p+1]; ++p; }
1216                                        }
1217                                        goto end_option;
1218                                default:
1219                                        goto invalid_argument;
1220                                }
1221                        }
1222 end_option:
1223                        continue;
1224                }
1225
1226                mp = find_mode(arg);
1227                if (mp) {
1228                        stty_state &= ~STTY_noargs;
1229                        continue;
1230                }
1231
1232                cp = find_control(arg);
1233                if (cp) {
1234                        if (!argnext)
1235                                bb_error_msg_and_die(bb_msg_requires_arg, arg);
1236                        /* called for the side effect of xfunc death only */
1237                        set_control_char_or_die(cp, argnext, &mode);
1238                        stty_state &= ~STTY_noargs;
1239                        ++k;
1240                        continue;
1241                }
1242
1243                param = find_param(arg);
1244                if (param & param_need_arg) {
1245                        if (!argnext)
1246                                bb_error_msg_and_die(bb_msg_requires_arg, arg);
1247                        ++k;
1248                }
1249
1250                switch (param) {
1251#ifdef HAVE_C_LINE
1252                case param_line:
1253# ifndef TIOCGWINSZ
1254                        xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1255                        break;
1256# endif /* else fall-through */
1257#endif
1258#ifdef TIOCGWINSZ
1259                case param_rows:
1260                case param_cols:
1261                case param_columns:
1262                        xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1263                        break;
1264                case param_size:
1265#endif
1266                case param_speed:
1267                        break;
1268                case param_ispeed:
1269                        /* called for the side effect of xfunc death only */
1270                        set_speed_or_die(input_speed, argnext, &mode);
1271                        break;
1272                case param_ospeed:
1273                        /* called for the side effect of xfunc death only */
1274                        set_speed_or_die(output_speed, argnext, &mode);
1275                        break;
1276                default:
1277                        if (recover_mode(arg, &mode) == 1) break;
1278                        if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1279 invalid_argument:
1280                        bb_error_msg_and_die("invalid argument '%s'", arg);
1281                }
1282                stty_state &= ~STTY_noargs;
1283        }
1284
1285        /* Specifying both -a and -g is an error */
1286        if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1287                (STTY_verbose_output | STTY_recoverable_output))
1288                bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
1289        /* Specifying -a or -g with non-options is an error */
1290        if (!(stty_state & STTY_noargs) &&
1291                (stty_state & (STTY_verbose_output | STTY_recoverable_output)))
1292                bb_error_msg_and_die("modes may not be set when specifying an output style");
1293
1294        /* Now it is safe to start doing things */
1295        if (file_name) {
1296                int fd, fdflags;
1297                G.device_name = file_name;
1298                fd = xopen(G.device_name, O_RDONLY | O_NONBLOCK);
1299                if (fd != STDIN_FILENO) {
1300                        dup2(fd, STDIN_FILENO);
1301                        close(fd);
1302                }
1303                fdflags = fcntl(STDIN_FILENO, F_GETFL);
1304                if (fdflags < 0 ||
1305                        fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
1306                        perror_on_device_and_die("%s: cannot reset non-blocking mode");
1307        }
1308
1309        /* Initialize to all zeroes so there is no risk memcmp will report a
1310           spurious difference in an uninitialized portion of the structure */
1311        memset(&mode, 0, sizeof(mode));
1312        if (tcgetattr(STDIN_FILENO, &mode))
1313                perror_on_device_and_die("%s");
1314
1315        if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
1316                get_terminal_width_height(STDOUT_FILENO, &G.max_col, NULL);
1317                output_func(&mode, display_all);
1318                return EXIT_SUCCESS;
1319        }
1320
1321        /* Second pass: perform actions */
1322        k = 0;
1323        while (argv[++k]) {
1324                const struct mode_info *mp;
1325                const struct control_info *cp;
1326                const char *arg = argv[k];
1327                const char *argnext = argv[k+1];
1328                int param;
1329
1330                if (arg[0] == '-') {
1331                        mp = find_mode(arg+1);
1332                        if (mp) {
1333                                set_mode(mp, 1 /* reversed */, &mode);
1334                                stty_state |= STTY_require_set_attr;
1335                        }
1336                        /* It is an option - already parsed. Skip it */
1337                        continue;
1338                }
1339
1340                mp = find_mode(arg);
1341                if (mp) {
1342                        set_mode(mp, 0 /* non-reversed */, &mode);
1343                        stty_state |= STTY_require_set_attr;
1344                        continue;
1345                }
1346
1347                cp = find_control(arg);
1348                if (cp) {
1349                        ++k;
1350                        set_control_char_or_die(cp, argnext, &mode);
1351                        stty_state |= STTY_require_set_attr;
1352                        continue;
1353                }
1354
1355                param = find_param(arg);
1356                if (param & param_need_arg) {
1357                        ++k;
1358                }
1359
1360                switch (param) {
1361#ifdef HAVE_C_LINE
1362                case param_line:
1363                        mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1364                        stty_state |= STTY_require_set_attr;
1365                        break;
1366#endif
1367#ifdef TIOCGWINSZ
1368                case param_cols:
1369                        set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1370                        break;
1371                case param_size:
1372                        display_window_size(0);
1373                        break;
1374                case param_rows:
1375                        set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1376                        break;
1377#endif
1378                case param_speed:
1379                        display_speed(&mode, 0);
1380                        break;
1381                case param_ispeed:
1382                        set_speed_or_die(input_speed, argnext, &mode);
1383                        stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1384                        break;
1385                case param_ospeed:
1386                        set_speed_or_die(output_speed, argnext, &mode);
1387                        stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1388                        break;
1389                default:
1390                        if (recover_mode(arg, &mode) == 1)
1391                                stty_state |= STTY_require_set_attr;
1392                        else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
1393                                set_speed_or_die(both_speeds, arg, &mode);
1394                                stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1395                        } /* else - impossible (caught in the first pass):
1396                                bb_error_msg_and_die("invalid argument '%s'", arg); */
1397                }
1398        }
1399
1400        if (stty_state & STTY_require_set_attr) {
1401                struct termios new_mode;
1402
1403                if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1404                        perror_on_device_and_die("%s");
1405
1406                /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1407                   it performs *any* of the requested operations.  This means it
1408                   can report 'success' when it has actually failed to perform
1409                   some proper subset of the requested operations.  To detect
1410                   this partial failure, get the current terminal attributes and
1411                   compare them to the requested ones */
1412
1413                /* Initialize to all zeroes so there is no risk memcmp will report a
1414                   spurious difference in an uninitialized portion of the structure */
1415                memset(&new_mode, 0, sizeof(new_mode));
1416                if (tcgetattr(STDIN_FILENO, &new_mode))
1417                        perror_on_device_and_die("%s");
1418
1419                if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1420#ifdef CIBAUD
1421                        /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1422                           tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1423                           sometimes (m1 != m2).  The only difference is in the four bits
1424                           of the c_cflag field corresponding to the baud rate.  To save
1425                           Sun users a little confusion, don't report an error if this
1426                           happens.  But suppress the error only if we haven't tried to
1427                           set the baud rate explicitly -- otherwise we'd never give an
1428                           error for a true failure to set the baud rate */
1429
1430                        new_mode.c_cflag &= (~CIBAUD);
1431                        if ((stty_state & STTY_speed_was_set)
1432                         || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1433#endif
1434                                perror_on_device_and_die("%s: cannot perform all requested operations");
1435                }
1436        }
1437
1438        return EXIT_SUCCESS;
1439}
1440