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