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