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