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