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.
7 /* Usage: stty [-ag] [-F device] [setting...]
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
26 #ifndef _POSIX_VDISABLE
27 # define _POSIX_VDISABLE ((unsigned char) 0)
30 #define Control(c) ((c) & 0x1f)
31 /* Canonical values for control characters */
33 # define CINTR Control('c')
42 # define CKILL Control('u')
45 # define CEOF Control('d')
48 # define CEOL _POSIX_VDISABLE
51 # define CSTART Control('q')
54 # define CSTOP Control('s')
57 # define CSUSP Control('z')
59 #if defined(VEOL2) && !defined(CEOL2)
60 # define CEOL2 _POSIX_VDISABLE
62 /* ISC renamed swtch to susp for termios, but we'll accept either name */
63 #if defined(VSUSP) && !defined(VSWTCH)
67 #if defined(VSWTCH) && !defined(CSWTCH)
68 # define CSWTCH _POSIX_VDISABLE
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__)
75 # define CSWTCH _POSIX_VDISABLE
78 #if defined(VWERSE) && !defined(VWERASE) /* AIX-3.2.5 */
79 # define VWERASE VWERSE
81 #if defined(VDSUSP) && !defined(CDSUSP)
82 # define CDSUSP Control('y')
84 #if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */
85 # define VREPRINT VRPRNT
87 #if defined(VREPRINT) && !defined(CRPRNT)
88 # define CRPRNT Control('r')
90 #if defined(VWERASE) && !defined(CWERASE)
91 # define CWERASE Control('w')
93 #if defined(VLNEXT) && !defined(CLNEXT)
94 # define CLNEXT Control('v')
96 #if defined(VDISCARD) && !defined(VFLUSHO)
97 # define VFLUSHO VDISCARD
99 #if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */
100 # define VFLUSHO VFLUSH
102 #if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */
103 # define ECHOCTL CTLECH
105 #if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */
106 # define ECHOCTL TCTLECH
108 #if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */
109 # define ECHOKE CRTKIL
111 #if defined(VFLUSHO) && !defined(CFLUSHO)
112 # define CFLUSHO Control('o')
114 #if defined(VSTATUS) && !defined(CSTATUS)
115 # define CSTATUS Control('t')
118 /* Which speeds to set */
120 input_speed, output_speed, both_speeds
123 /* Which member(s) of 'struct termios' a mode uses */
125 /* Do NOT change the order or values, as mode_type_flag()
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 */
138 * This structure should be kept as small as humanly possible.
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
146 #elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000
149 const tcflag_t mask; /* Other bits to turn off for this mode */
151 /* was using short here, but ppc32 was unhappy */
152 const tcflag_t bits; /* Bits to set for this mode */
156 /* Must match mode_name[] and mode_info[] order! */
173 #if defined(TABDLY) || defined(OXTABS)
176 #if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
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 )
200 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
202 #if defined(TABDLY) || defined(OXTABS)
203 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
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 )
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 )
221 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
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 )
236 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
239 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
242 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
244 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
246 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
249 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
252 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
255 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
258 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
261 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
264 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
267 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
268 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
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)
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)
284 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
289 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
290 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
293 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
294 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
297 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
298 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
300 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
301 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
303 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
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 )
312 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
315 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
318 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
319 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 )
322 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
323 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 )
326 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
327 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 )
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 )
350 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
352 #if defined(TABDLY) || defined(OXTABS)
353 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
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 )
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 )
371 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
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 )
386 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
389 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
392 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
394 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
396 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
399 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
402 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
405 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
408 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
411 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
414 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
417 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
418 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
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)
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)
434 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
439 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
440 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
443 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
444 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
447 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
448 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
450 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
451 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
453 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
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 )
462 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
465 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
468 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
469 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 )
472 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
473 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 )
476 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
477 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 )
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 */
493 /* Must match control_name[] and control_info[] order! */
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 )
542 CI_ENTRY("eol2", CEOL2, VEOL2 )
545 CI_ENTRY("swtch", CSWTCH, VSWTCH )
547 CI_ENTRY("start", CSTART, VSTART )
548 CI_ENTRY("stop", CSTOP, VSTOP )
549 CI_ENTRY("susp", CSUSP, VSUSP )
551 CI_ENTRY("dsusp", CDSUSP, VDSUSP )
554 CI_ENTRY("rprnt", CRPRNT, VREPRINT)
557 CI_ENTRY("werase", CWERASE, VWERASE )
560 CI_ENTRY("lnext", CLNEXT, VLNEXT )
563 CI_ENTRY("flush", CFLUSHO, VFLUSHO )
566 CI_ENTRY("status", CSTATUS, VSTATUS )
568 /* These must be last because of the display routines */
569 CI_ENTRY("min", 1, VMIN )
570 CI_ENTRY("time", 0, VTIME )
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 )
585 CI_ENTRY("eol2", CEOL2, VEOL2 )
588 CI_ENTRY("swtch", CSWTCH, VSWTCH )
590 CI_ENTRY("start", CSTART, VSTART )
591 CI_ENTRY("stop", CSTOP, VSTOP )
592 CI_ENTRY("susp", CSUSP, VSUSP )
594 CI_ENTRY("dsusp", CDSUSP, VDSUSP )
597 CI_ENTRY("rprnt", CRPRNT, VREPRINT)
600 CI_ENTRY("werase", CWERASE, VWERASE )
603 CI_ENTRY("lnext", CLNEXT, VLNEXT )
606 CI_ENTRY("flush", CFLUSHO, VFLUSHO )
609 CI_ENTRY("status", CSTATUS, VSTATUS )
611 /* These must be last because of the display routines */
612 CI_ENTRY("min", 1, VMIN )
613 CI_ENTRY("time", 0, VTIME )
617 NUM_control_info = ARRAY_SIZE(control_info)
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;
629 #define G (*(struct globals*)&bb_common_bufsiz1)
632 G.device_name = bb_msg_standard_input; \
637 /* Return a string that is the printable representation of character CH */
638 /* Adapted from 'cat' by Torbjorn Granlund */
639 static const char *visible(unsigned ch)
643 if (ch == _POSIX_VDISABLE)
655 } else if (ch < 127) {
666 static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
668 static const uint8_t tcflag_offsets[] ALIGN1 = {
669 offsetof(struct termios, c_cflag), /* control */
670 offsetof(struct termios, c_iflag), /* input */
671 offsetof(struct termios, c_oflag), /* output */
672 offsetof(struct termios, c_lflag) /* local */
676 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
681 static void set_speed_or_die(enum speed_setting type, const char *const arg,
682 struct termios * const mode)
686 baud = tty_value_to_baud(xatou(arg));
688 if (type != output_speed) { /* either input or both */
689 cfsetispeed(mode, baud);
691 if (type != input_speed) { /* either output or both */
692 cfsetospeed(mode, baud);
696 static ATTRIBUTE_NORETURN void perror_on_device_and_die(const char *fmt)
698 bb_perror_msg_and_die(fmt, G.device_name);
701 static void perror_on_device(const char *fmt)
703 bb_perror_msg(fmt, G.device_name);
706 /* Print format string MESSAGE and optional args.
707 Wrap to next line first if it won't fit.
708 Print a space first unless MESSAGE will start a new line */
709 static void wrapf(const char *message, ...)
715 va_start(args, message);
716 buflen = vsnprintf(buf, sizeof(buf), message, args);
718 /* We seem to be called only with suitable lengths, but check if
719 somebody failed to adhere to this assumption just to be sure. */
720 if (!buflen || buflen >= sizeof(buf)) return;
722 if (G.current_col > 0) {
724 if (buf[0] != '\n') {
725 if (G.current_col + buflen >= G.max_col) {
733 G.current_col += buflen;
734 if (buf[buflen-1] == '\n')
738 static void set_window_size(const int rows, const int cols)
740 struct winsize win = { 0, 0, 0, 0 };
742 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
743 if (errno != EINVAL) {
746 memset(&win, 0, sizeof(win));
754 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
756 perror_on_device("%s");
759 static void display_window_size(const int fancy)
761 const char *fmt_str = "%s\0%s: no size information for this device";
762 unsigned width, height;
764 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
765 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
766 perror_on_device(fmt_str);
769 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
774 static const struct suffix_mult stty_suffixes[] = {
781 static const struct mode_info *find_mode(const char *name)
783 int i = index_in_strings(mode_name, name);
784 return i >= 0 ? &mode_info[i] : NULL;
787 static const struct control_info *find_control(const char *name)
789 int i = index_in_strings(control_name, name);
790 return i >= 0 ? &control_info[i] : NULL;
794 param_need_arg = 0x80,
795 param_line = 1 | 0x80,
796 param_rows = 2 | 0x80,
797 param_cols = 3 | 0x80,
798 param_columns = 4 | 0x80,
801 param_ispeed = 7 | 0x80,
802 param_ospeed = 8 | 0x80,
805 static int find_param(const char *const name)
807 static const char params[] ALIGN1 =
816 int i = index_in_strings(params, name) + 1;
819 if (i != 5 && i != 6)
824 static int recover_mode(const char *arg, struct termios *mode)
828 unsigned long iflag, oflag, cflag, lflag;
830 /* Scan into temporaries since it is too much trouble to figure out
831 the right format for 'tcflag_t' */
832 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
833 &iflag, &oflag, &cflag, &lflag, &n) != 4)
835 mode->c_iflag = iflag;
836 mode->c_oflag = oflag;
837 mode->c_cflag = cflag;
838 mode->c_lflag = lflag;
840 for (i = 0; i < NCCS; ++i) {
841 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
847 /* Fail if there are too many fields */
854 static void display_recoverable(const struct termios *mode,
855 int ATTRIBUTE_UNUSED dummy)
858 printf("%lx:%lx:%lx:%lx",
859 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
860 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
861 for (i = 0; i < NCCS; ++i)
862 printf(":%x", (unsigned int) mode->c_cc[i]);
866 static void display_speed(const struct termios *mode, int fancy)
869 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
870 unsigned long ispeed, ospeed;
872 ospeed = ispeed = cfgetispeed(mode);
873 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
874 ispeed = ospeed; /* in case ispeed was 0 */
876 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
878 if (fancy) fmt_str += 9;
879 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
882 static void do_display(const struct termios *mode, const int all)
887 int prev_type = control;
889 display_speed(mode, 1);
891 display_window_size(1);
893 wrapf("line = %d;\n", mode->c_line);
898 for (i = 0; i != CIDX_min; ++i) {
899 /* If swtch is the same as susp, don't print both */
904 /* If eof uses the same slot as min, only print whichever applies */
906 if ((mode->c_lflag & ICANON) == 0
907 && (i == CIDX_eof || i == CIDX_eol)
912 wrapf("%s = %s;", nth_string(control_name, i),
913 visible(mode->c_cc[control_info[i].offset]));
916 if ((mode->c_lflag & ICANON) == 0)
918 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
919 if (G.current_col) wrapf("\n");
921 for (i = 0; i < NUM_mode_info; ++i) {
922 if (mode_info[i].flags & OMIT)
924 if (mode_info[i].type != prev_type) {
926 if (G.current_col) wrapf("\n");
927 prev_type = mode_info[i].type;
930 bitsp = mode_type_flag(mode_info[i].type, mode);
931 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
932 if ((*bitsp & mask) == mode_info[i].bits) {
933 if (all || (mode_info[i].flags & SANE_UNSET))
934 wrapf("-%s"+1, nth_string(mode_name, i));
936 if ((all && mode_info[i].flags & REV)
937 || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
939 wrapf("-%s", nth_string(mode_name, i));
943 if (G.current_col) wrapf("\n");
946 static void sane_mode(struct termios *mode)
951 for (i = 0; i < NUM_control_info; ++i) {
956 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
959 for (i = 0; i < NUM_mode_info; ++i) {
960 if (mode_info[i].flags & SANE_SET) {
961 bitsp = mode_type_flag(mode_info[i].type, mode);
962 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
964 } else if (mode_info[i].flags & SANE_UNSET) {
965 bitsp = mode_type_flag(mode_info[i].type, mode);
966 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
967 & ~mode_info[i].bits;
972 /* Save set_mode from #ifdef forest plague */
1007 static void set_mode(const struct mode_info *info, int reversed,
1008 struct termios *mode)
1012 bitsp = mode_type_flag(info->type, mode);
1016 *bitsp = *bitsp & ~info->mask & ~info->bits;
1018 *bitsp = (*bitsp & ~info->mask) | info->bits;
1022 /* Combination mode */
1023 if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
1025 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1027 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
1028 } else if (info == &mode_info[IDX_oddp]) {
1030 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1032 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
1033 } else if (info == &mode_info[IDX_nl]) {
1035 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
1036 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
1038 mode->c_iflag = mode->c_iflag & ~ICRNL;
1039 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1041 } else if (info == &mode_info[IDX_ek]) {
1042 mode->c_cc[VERASE] = CERASE;
1043 mode->c_cc[VKILL] = CKILL;
1044 } else if (info == &mode_info[IDX_sane]) {
1046 } else if (info == &mode_info[IDX_cbreak]) {
1048 mode->c_lflag |= ICANON;
1050 mode->c_lflag &= ~ICANON;
1051 } else if (info == &mode_info[IDX_pass8]) {
1053 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1054 mode->c_iflag |= ISTRIP;
1056 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1057 mode->c_iflag &= ~ISTRIP;
1059 } else if (info == &mode_info[IDX_litout]) {
1061 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1062 mode->c_iflag |= ISTRIP;
1063 mode->c_oflag |= OPOST;
1065 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1066 mode->c_iflag &= ~ISTRIP;
1067 mode->c_oflag &= ~OPOST;
1069 } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
1070 if ((info == &mode_info[IDX_raw] && reversed)
1071 || (info == &mode_info[IDX_cooked] && !reversed)
1074 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1075 mode->c_oflag |= OPOST;
1076 mode->c_lflag |= ISIG | ICANON;
1078 mode->c_cc[VEOF] = CEOF;
1081 mode->c_cc[VEOL] = CEOL;
1086 mode->c_oflag &= ~OPOST;
1087 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1088 mode->c_cc[VMIN] = 1;
1089 mode->c_cc[VTIME] = 0;
1092 else if (IXANY && info == &mode_info[IDX_decctlq]) {
1094 mode->c_iflag |= IXANY;
1096 mode->c_iflag &= ~IXANY;
1098 else if (TABDLY && info == &mode_info[IDX_tabs]) {
1100 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1102 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1104 else if (OXTABS && info == &mode_info[IDX_tabs]) {
1106 mode->c_oflag |= OXTABS;
1108 mode->c_oflag &= ~OXTABS;
1110 if (XCASE && IUCLC && OLCUC
1111 && (info == &mode_info[IDX_lcase] || info == &mode_info[IDX_LCASE])
1114 mode->c_lflag &= ~XCASE;
1115 mode->c_iflag &= ~IUCLC;
1116 mode->c_oflag &= ~OLCUC;
1118 mode->c_lflag |= XCASE;
1119 mode->c_iflag |= IUCLC;
1120 mode->c_oflag |= OLCUC;
1122 } else if (info == &mode_info[IDX_crt]) {
1123 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1124 } else if (info == &mode_info[IDX_dec]) {
1125 mode->c_cc[VINTR] = 3; /* ^C */
1126 mode->c_cc[VERASE] = 127; /* DEL */
1127 mode->c_cc[VKILL] = 21; /* ^U */
1128 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1129 if (IXANY) mode->c_iflag &= ~IXANY;
1133 static void set_control_char_or_die(const struct control_info *info,
1134 const char *arg, struct termios *mode)
1136 unsigned char value;
1138 if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
1139 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1140 else if (arg[0] == '\0' || arg[1] == '\0')
1142 else if (!strcmp(arg, "^-") || !strcmp(arg, "undef"))
1143 value = _POSIX_VDISABLE;
1144 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1145 value = arg[1] & 0x1f; /* Non-letters get weird results */
1149 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1150 mode->c_cc[info->offset] = value;
1153 #define STTY_require_set_attr (1 << 0)
1154 #define STTY_speed_was_set (1 << 1)
1155 #define STTY_verbose_output (1 << 2)
1156 #define STTY_recoverable_output (1 << 3)
1157 #define STTY_noargs (1 << 4)
1159 int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1160 int stty_main(int argc, char **argv)
1162 struct termios mode;
1163 void (*output_func)(const struct termios *, const int);
1164 const char *file_name = NULL;
1165 int display_all = 0;
1171 stty_state = STTY_noargs;
1172 output_func = do_display;
1174 /* First pass: only parse/verify command line params */
1177 const struct mode_info *mp;
1178 const struct control_info *cp;
1179 const char *arg = argv[k];
1180 const char *argnext = argv[k+1];
1183 if (arg[0] == '-') {
1185 mp = find_mode(arg+1);
1187 if (!(mp->flags & REV))
1188 goto invalid_argument;
1189 stty_state &= ~STTY_noargs;
1192 /* It is an option - parse it */
1197 stty_state |= STTY_verbose_output;
1198 output_func = do_display;
1202 stty_state |= STTY_recoverable_output;
1203 output_func = display_recoverable;
1207 bb_error_msg_and_die("only one device may be specified");
1208 file_name = &arg[i+1]; /* "-Fdevice" ? */
1209 if (!file_name[0]) { /* nope, "-F device" */
1210 int p = k+1; /* argv[p] is argnext */
1211 file_name = argnext;
1213 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1214 /* remove -F param from arg[vc] */
1216 while (argv[p]) { argv[p] = argv[p+1]; ++p; }
1220 goto invalid_argument;
1227 mp = find_mode(arg);
1229 stty_state &= ~STTY_noargs;
1233 cp = find_control(arg);
1236 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1237 /* called for the side effect of xfunc death only */
1238 set_control_char_or_die(cp, argnext, &mode);
1239 stty_state &= ~STTY_noargs;
1244 param = find_param(arg);
1245 if (param & param_need_arg) {
1247 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1255 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1257 # endif /* else fall-through */
1263 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1270 /* called for the side effect of xfunc death only */
1271 set_speed_or_die(input_speed, argnext, &mode);
1274 /* called for the side effect of xfunc death only */
1275 set_speed_or_die(output_speed, argnext, &mode);
1278 if (recover_mode(arg, &mode) == 1) break;
1279 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1281 bb_error_msg_and_die("invalid argument '%s'", arg);
1283 stty_state &= ~STTY_noargs;
1286 /* Specifying both -a and -g is an error */
1287 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1288 (STTY_verbose_output | STTY_recoverable_output))
1289 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
1290 /* Specifying -a or -g with non-options is an error */
1291 if (!(stty_state & STTY_noargs) &&
1292 (stty_state & (STTY_verbose_output | STTY_recoverable_output)))
1293 bb_error_msg_and_die("modes may not be set when specifying an output style");
1295 /* Now it is safe to start doing things */
1298 G.device_name = file_name;
1299 fd = xopen(G.device_name, O_RDONLY | O_NONBLOCK);
1300 if (fd != STDIN_FILENO) {
1301 dup2(fd, STDIN_FILENO);
1304 fdflags = fcntl(STDIN_FILENO, F_GETFL);
1306 fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
1307 perror_on_device_and_die("%s: cannot reset non-blocking mode");
1310 /* Initialize to all zeroes so there is no risk memcmp will report a
1311 spurious difference in an uninitialized portion of the structure */
1312 memset(&mode, 0, sizeof(mode));
1313 if (tcgetattr(STDIN_FILENO, &mode))
1314 perror_on_device_and_die("%s");
1316 if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
1317 get_terminal_width_height(STDOUT_FILENO, &G.max_col, NULL);
1318 output_func(&mode, display_all);
1319 return EXIT_SUCCESS;
1322 /* Second pass: perform actions */
1325 const struct mode_info *mp;
1326 const struct control_info *cp;
1327 const char *arg = argv[k];
1328 const char *argnext = argv[k+1];
1331 if (arg[0] == '-') {
1332 mp = find_mode(arg+1);
1334 set_mode(mp, 1 /* reversed */, &mode);
1335 stty_state |= STTY_require_set_attr;
1337 /* It is an option - already parsed. Skip it */
1341 mp = find_mode(arg);
1343 set_mode(mp, 0 /* non-reversed */, &mode);
1344 stty_state |= STTY_require_set_attr;
1348 cp = find_control(arg);
1351 set_control_char_or_die(cp, argnext, &mode);
1352 stty_state |= STTY_require_set_attr;
1356 param = find_param(arg);
1357 if (param & param_need_arg) {
1364 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1365 stty_state |= STTY_require_set_attr;
1370 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1373 display_window_size(0);
1376 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1380 display_speed(&mode, 0);
1383 set_speed_or_die(input_speed, argnext, &mode);
1384 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1387 set_speed_or_die(output_speed, argnext, &mode);
1388 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1391 if (recover_mode(arg, &mode) == 1)
1392 stty_state |= STTY_require_set_attr;
1393 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
1394 set_speed_or_die(both_speeds, arg, &mode);
1395 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1396 } /* else - impossible (caught in the first pass):
1397 bb_error_msg_and_die("invalid argument '%s'", arg); */
1401 if (stty_state & STTY_require_set_attr) {
1402 struct termios new_mode;
1404 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1405 perror_on_device_and_die("%s");
1407 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1408 it performs *any* of the requested operations. This means it
1409 can report 'success' when it has actually failed to perform
1410 some proper subset of the requested operations. To detect
1411 this partial failure, get the current terminal attributes and
1412 compare them to the requested ones */
1414 /* Initialize to all zeroes so there is no risk memcmp will report a
1415 spurious difference in an uninitialized portion of the structure */
1416 memset(&new_mode, 0, sizeof(new_mode));
1417 if (tcgetattr(STDIN_FILENO, &new_mode))
1418 perror_on_device_and_die("%s");
1420 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1422 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1423 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1424 sometimes (m1 != m2). The only difference is in the four bits
1425 of the c_cflag field corresponding to the baud rate. To save
1426 Sun users a little confusion, don't report an error if this
1427 happens. But suppress the error only if we haven't tried to
1428 set the baud rate explicitly -- otherwise we'd never give an
1429 error for a true failure to set the baud rate */
1431 new_mode.c_cflag &= (~CIBAUD);
1432 if ((stty_state & STTY_speed_was_set)
1433 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1435 perror_on_device_and_die("%s: cannot perform all requested operations");
1439 return EXIT_SUCCESS;