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