source: branches/stable/mindi-busybox/coreutils/stty.c @ 1770

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 13 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod? silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian? keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René? Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

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