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