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