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