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