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