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