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