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