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