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