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