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 GPLv2 or later, see file LICENSE in this source tree.
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 /* glibc-2.12.1 uses only VSWTC name */
63 #if defined(VSWTC) && !defined(VSWTCH)
66 /* ISC renamed swtch to susp for termios, but we'll accept either name */
67 #if defined(VSUSP) && !defined(VSWTCH)
71 #if defined(VSWTCH) && !defined(CSWTCH)
72 # define CSWTCH _POSIX_VDISABLE
75 /* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'.
76 So the default is to disable 'swtch.' */
77 #if defined(__sparc__) && defined(__svr4__)
79 # define CSWTCH _POSIX_VDISABLE
82 #if defined(VWERSE) && !defined(VWERASE) /* AIX-3.2.5 */
83 # define VWERASE VWERSE
85 #if defined(VDSUSP) && !defined(CDSUSP)
86 # define CDSUSP Control('y')
88 #if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */
89 # define VREPRINT VRPRNT
91 #if defined(VREPRINT) && !defined(CRPRNT)
92 # define CRPRNT Control('r')
94 #if defined(VWERASE) && !defined(CWERASE)
95 # define CWERASE Control('w')
97 #if defined(VLNEXT) && !defined(CLNEXT)
98 # define CLNEXT Control('v')
100 #if defined(VDISCARD) && !defined(VFLUSHO)
101 # define VFLUSHO VDISCARD
103 #if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */
104 # define VFLUSHO VFLUSH
106 #if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */
107 # define ECHOCTL CTLECH
109 #if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */
110 # define ECHOCTL TCTLECH
112 #if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */
113 # define ECHOKE CRTKIL
115 #if defined(VFLUSHO) && !defined(CFLUSHO)
116 # define CFLUSHO Control('o')
118 #if defined(VSTATUS) && !defined(CSTATUS)
119 # define CSTATUS Control('t')
122 /* Save us from #ifdef forest plague */
232 /* Which speeds to set */
234 input_speed, output_speed, both_speeds
237 /* Which member(s) of 'struct termios' a mode uses */
239 /* Do NOT change the order or values, as mode_type_flag()
241 control, input, output, local, combination
244 /* Flags for 'struct mode_info' */
245 #define SANE_SET 1 /* Set in 'sane' mode */
246 #define SANE_UNSET 2 /* Unset in 'sane' mode */
247 #define REV 4 /* Can be turned off by prepending '-' */
248 #define OMIT 8 /* Don't display value */
252 * This structure should be kept as small as humanly possible.
255 const uint8_t type; /* Which structure element to change */
256 const uint8_t flags; /* Setting and display options */
257 /* only these values are ever used, so... */
258 #if (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100
260 #elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000
263 const tcflag_t mask; /* Other bits to turn off for this mode */
265 /* was using short here, but ppc32 was unhappy */
266 const tcflag_t bits; /* Bits to set for this mode */
270 /* Must match mode_name[] and mode_info[] order! */
290 #if XCASE && IUCLC && OLCUC
296 #define MI_ENTRY(N,T,F,B,M) N "\0"
298 /* Mode names given on command line */
299 static const char mode_name[] =
300 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
301 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
302 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
303 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
304 MI_ENTRY("ek", combination, OMIT, 0, 0 )
305 MI_ENTRY("sane", combination, OMIT, 0, 0 )
306 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
307 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
308 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
309 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
310 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
311 MI_ENTRY("crt", combination, OMIT, 0, 0 )
312 MI_ENTRY("dec", combination, OMIT, 0, 0 )
314 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
317 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
319 #if XCASE && IUCLC && OLCUC
320 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 )
321 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 )
323 MI_ENTRY("parenb", control, REV, PARENB, 0 )
324 MI_ENTRY("parodd", control, REV, PARODD, 0 )
325 MI_ENTRY("cs5", control, 0, CS5, CSIZE)
326 MI_ENTRY("cs6", control, 0, CS6, CSIZE)
327 MI_ENTRY("cs7", control, 0, CS7, CSIZE)
328 MI_ENTRY("cs8", control, 0, CS8, CSIZE)
329 MI_ENTRY("hupcl", control, REV, HUPCL, 0 )
330 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 )
331 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 )
332 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 )
333 MI_ENTRY("clocal", control, REV, CLOCAL, 0 )
335 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
337 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 )
338 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 )
339 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 )
340 MI_ENTRY("parmrk", input, REV, PARMRK, 0 )
341 MI_ENTRY("inpck", input, REV, INPCK, 0 )
342 MI_ENTRY("istrip", input, REV, ISTRIP, 0 )
343 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 )
344 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 )
345 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
346 MI_ENTRY("ixon", input, REV, IXON, 0 )
347 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
348 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 )
350 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
353 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
356 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
359 MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 )
361 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
363 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
366 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
369 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
372 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
375 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
378 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
381 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
384 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
385 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
388 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY)
389 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY)
390 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY)
391 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY)
395 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY)
397 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY)
400 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY)
402 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY)
405 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
410 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
411 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
414 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
415 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
418 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
419 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
421 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
422 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
424 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
426 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
427 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
428 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 )
429 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
430 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
431 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
433 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
436 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
439 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
440 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 )
443 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
444 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 )
447 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
448 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 )
453 #define MI_ENTRY(N,T,F,B,M) { T, F, M, B },
455 static const struct mode_info mode_info[] = {
456 /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */
457 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
458 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
459 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
460 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
461 MI_ENTRY("ek", combination, OMIT, 0, 0 )
462 MI_ENTRY("sane", combination, OMIT, 0, 0 )
463 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
464 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
465 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
466 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
467 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
468 MI_ENTRY("crt", combination, OMIT, 0, 0 )
469 MI_ENTRY("dec", combination, OMIT, 0, 0 )
471 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
474 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
476 #if XCASE && IUCLC && OLCUC
477 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 )
478 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 )
480 MI_ENTRY("parenb", control, REV, PARENB, 0 )
481 MI_ENTRY("parodd", control, REV, PARODD, 0 )
482 MI_ENTRY("cs5", control, 0, CS5, CSIZE)
483 MI_ENTRY("cs6", control, 0, CS6, CSIZE)
484 MI_ENTRY("cs7", control, 0, CS7, CSIZE)
485 MI_ENTRY("cs8", control, 0, CS8, CSIZE)
486 MI_ENTRY("hupcl", control, REV, HUPCL, 0 )
487 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 )
488 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 )
489 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 )
490 MI_ENTRY("clocal", control, REV, CLOCAL, 0 )
492 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
494 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 )
495 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 )
496 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 )
497 MI_ENTRY("parmrk", input, REV, PARMRK, 0 )
498 MI_ENTRY("inpck", input, REV, INPCK, 0 )
499 MI_ENTRY("istrip", input, REV, ISTRIP, 0 )
500 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 )
501 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 )
502 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
503 MI_ENTRY("ixon", input, REV, IXON, 0 )
504 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
505 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 )
507 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
510 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
513 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
516 MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 )
518 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
520 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
523 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
526 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
529 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
532 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
535 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
538 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
541 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
542 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
545 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY)
546 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY)
547 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY)
548 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY)
552 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY)
554 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY)
557 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY)
559 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY)
562 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
567 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
568 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
571 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
572 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
575 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
576 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
578 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
579 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
581 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
583 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
584 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
585 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 )
586 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
587 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
588 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
590 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
593 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
596 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
597 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 )
600 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
601 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 )
604 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
605 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 )
610 NUM_mode_info = ARRAY_SIZE(mode_info)
614 /* Control characters */
615 struct control_info {
616 const uint8_t saneval; /* Value to set for 'stty sane' */
617 const uint8_t offset; /* Offset in c_cc */
621 /* Must match control_name[] and control_info[] order! */
659 #define CI_ENTRY(n,s,o) n "\0"
661 /* Name given on command line */
662 static const char control_name[] =
663 CI_ENTRY("intr", CINTR, VINTR )
664 CI_ENTRY("quit", CQUIT, VQUIT )
665 CI_ENTRY("erase", CERASE, VERASE )
666 CI_ENTRY("kill", CKILL, VKILL )
667 CI_ENTRY("eof", CEOF, VEOF )
668 CI_ENTRY("eol", CEOL, VEOL )
670 CI_ENTRY("eol2", CEOL2, VEOL2 )
673 CI_ENTRY("swtch", CSWTCH, VSWTCH )
675 CI_ENTRY("start", CSTART, VSTART )
676 CI_ENTRY("stop", CSTOP, VSTOP )
677 CI_ENTRY("susp", CSUSP, VSUSP )
679 CI_ENTRY("dsusp", CDSUSP, VDSUSP )
682 CI_ENTRY("rprnt", CRPRNT, VREPRINT)
685 CI_ENTRY("werase", CWERASE, VWERASE )
688 CI_ENTRY("lnext", CLNEXT, VLNEXT )
691 CI_ENTRY("flush", CFLUSHO, VFLUSHO )
694 CI_ENTRY("status", CSTATUS, VSTATUS )
696 /* These must be last because of the display routines */
697 CI_ENTRY("min", 1, VMIN )
698 CI_ENTRY("time", 0, VTIME )
702 #define CI_ENTRY(n,s,o) { s, o },
704 static const struct control_info control_info[] = {
705 /* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */
706 CI_ENTRY("intr", CINTR, VINTR )
707 CI_ENTRY("quit", CQUIT, VQUIT )
708 CI_ENTRY("erase", CERASE, VERASE )
709 CI_ENTRY("kill", CKILL, VKILL )
710 CI_ENTRY("eof", CEOF, VEOF )
711 CI_ENTRY("eol", CEOL, VEOL )
713 CI_ENTRY("eol2", CEOL2, VEOL2 )
716 CI_ENTRY("swtch", CSWTCH, VSWTCH )
718 CI_ENTRY("start", CSTART, VSTART )
719 CI_ENTRY("stop", CSTOP, VSTOP )
720 CI_ENTRY("susp", CSUSP, VSUSP )
722 CI_ENTRY("dsusp", CDSUSP, VDSUSP )
725 CI_ENTRY("rprnt", CRPRNT, VREPRINT)
728 CI_ENTRY("werase", CWERASE, VWERASE )
731 CI_ENTRY("lnext", CLNEXT, VLNEXT )
734 CI_ENTRY("flush", CFLUSHO, VFLUSHO )
737 CI_ENTRY("status", CSTATUS, VSTATUS )
739 /* These must be last because of the display routines */
740 CI_ENTRY("min", 1, VMIN )
741 CI_ENTRY("time", 0, VTIME )
745 NUM_control_info = ARRAY_SIZE(control_info)
750 const char *device_name;
751 /* The width of the screen, for output wrapping */
753 /* Current position, to know when to wrap */
754 unsigned current_col;
757 #define G (*(struct globals*)&bb_common_bufsiz1)
758 #define INIT_G() do { \
759 G.device_name = bb_msg_standard_input; \
764 /* Return a string that is the printable representation of character CH */
765 /* Adapted from 'cat' by Torbjorn Granlund */
766 static const char *visible(unsigned ch)
770 if (ch == _POSIX_VDISABLE)
782 } else if (ch < 127) {
793 static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
795 static const uint8_t tcflag_offsets[] ALIGN1 = {
796 offsetof(struct termios, c_cflag), /* control */
797 offsetof(struct termios, c_iflag), /* input */
798 offsetof(struct termios, c_oflag), /* output */
799 offsetof(struct termios, c_lflag) /* local */
803 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
808 static void set_speed_or_die(enum speed_setting type, const char *arg,
809 struct termios *mode)
813 baud = tty_value_to_baud(xatou(arg));
815 if (type != output_speed) { /* either input or both */
816 cfsetispeed(mode, baud);
818 if (type != input_speed) { /* either output or both */
819 cfsetospeed(mode, baud);
823 static NORETURN void perror_on_device_and_die(const char *fmt)
825 bb_perror_msg_and_die(fmt, G.device_name);
828 static void perror_on_device(const char *fmt)
830 bb_perror_msg(fmt, G.device_name);
833 /* Print format string MESSAGE and optional args.
834 Wrap to next line first if it won't fit.
835 Print a space first unless MESSAGE will start a new line */
836 static void wrapf(const char *message, ...)
842 va_start(args, message);
843 buflen = vsnprintf(buf, sizeof(buf), message, args);
845 /* We seem to be called only with suitable lengths, but check if
846 somebody failed to adhere to this assumption just to be sure. */
847 if (!buflen || buflen >= sizeof(buf)) return;
849 if (G.current_col > 0) {
851 if (buf[0] != '\n') {
852 if (G.current_col + buflen >= G.max_col) {
860 G.current_col += buflen;
861 if (buf[buflen-1] == '\n')
865 static void newline(void)
867 if (G.current_col != 0)
872 static void set_window_size(int rows, int cols)
874 struct winsize win = { 0, 0, 0, 0 };
876 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
877 if (errno != EINVAL) {
880 memset(&win, 0, sizeof(win));
888 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
890 perror_on_device("%s");
894 static void display_window_size(int fancy)
896 const char *fmt_str = "%s\0%s: no size information for this device";
897 unsigned width, height;
899 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
900 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
901 perror_on_device(fmt_str);
904 wrapf(fancy ? "rows %u; columns %u;" : "%u %u\n",
909 static const struct suffix_mult stty_suffixes[] = {
916 static const struct mode_info *find_mode(const char *name)
918 int i = index_in_strings(mode_name, name);
919 return i >= 0 ? &mode_info[i] : NULL;
922 static const struct control_info *find_control(const char *name)
924 int i = index_in_strings(control_name, name);
925 return i >= 0 ? &control_info[i] : NULL;
929 param_need_arg = 0x80,
930 param_line = 1 | 0x80,
931 param_rows = 2 | 0x80,
932 param_cols = 3 | 0x80,
933 param_columns = 4 | 0x80,
936 param_ispeed = 7 | 0x80,
937 param_ospeed = 8 | 0x80,
940 static int find_param(const char *name)
942 static const char params[] ALIGN1 =
951 int i = index_in_strings(params, name) + 1;
954 if (i != 5 && i != 6)
959 static int recover_mode(const char *arg, struct termios *mode)
963 unsigned long iflag, oflag, cflag, lflag;
965 /* Scan into temporaries since it is too much trouble to figure out
966 the right format for 'tcflag_t' */
967 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
968 &iflag, &oflag, &cflag, &lflag, &n) != 4)
970 mode->c_iflag = iflag;
971 mode->c_oflag = oflag;
972 mode->c_cflag = cflag;
973 mode->c_lflag = lflag;
975 for (i = 0; i < NCCS; ++i) {
976 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
982 /* Fail if there are too many fields */
989 static void display_recoverable(const struct termios *mode,
990 int UNUSED_PARAM dummy)
993 printf("%lx:%lx:%lx:%lx",
994 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
995 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
996 for (i = 0; i < NCCS; ++i)
997 printf(":%x", (unsigned int) mode->c_cc[i]);
1001 static void display_speed(const struct termios *mode, int fancy)
1003 //____________________ 01234567 8 9
1004 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
1005 unsigned long ispeed, ospeed;
1007 ospeed = ispeed = cfgetispeed(mode);
1008 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
1009 ispeed = ospeed; /* in case ispeed was 0 */
1010 //________ 0123 4 5 6 7 8 9
1011 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
1013 if (fancy) fmt_str += 9;
1014 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
1017 static void do_display(const struct termios *mode, int all)
1022 int prev_type = control;
1024 display_speed(mode, 1);
1026 display_window_size(1);
1028 wrapf("line = %u;\n", mode->c_line);
1033 for (i = 0; i != CIDX_min; ++i) {
1034 /* If swtch is the same as susp, don't print both */
1036 if (i == CIDX_swtch)
1039 /* If eof uses the same slot as min, only print whichever applies */
1041 if (!(mode->c_lflag & ICANON)
1042 && (i == CIDX_eof || i == CIDX_eol)
1047 wrapf("%s = %s;", nth_string(control_name, i),
1048 visible(mode->c_cc[control_info[i].offset]));
1051 if ((mode->c_lflag & ICANON) == 0)
1053 wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1056 for (i = 0; i < NUM_mode_info; ++i) {
1057 if (mode_info[i].flags & OMIT)
1059 if (mode_info[i].type != prev_type) {
1061 prev_type = mode_info[i].type;
1064 bitsp = mode_type_flag(mode_info[i].type, mode);
1065 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1066 if ((*bitsp & mask) == mode_info[i].bits) {
1067 if (all || (mode_info[i].flags & SANE_UNSET))
1068 wrapf("-%s"+1, nth_string(mode_name, i));
1070 if ((all && mode_info[i].flags & REV)
1071 || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
1073 wrapf("-%s", nth_string(mode_name, i));
1080 static void sane_mode(struct termios *mode)
1085 for (i = 0; i < NUM_control_info; ++i) {
1090 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1093 for (i = 0; i < NUM_mode_info; ++i) {
1094 if (mode_info[i].flags & SANE_SET) {
1095 bitsp = mode_type_flag(mode_info[i].type, mode);
1096 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
1097 | mode_info[i].bits;
1098 } else if (mode_info[i].flags & SANE_UNSET) {
1099 bitsp = mode_type_flag(mode_info[i].type, mode);
1100 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
1101 & ~mode_info[i].bits;
1106 static void set_mode(const struct mode_info *info, int reversed,
1107 struct termios *mode)
1111 bitsp = mode_type_flag(info->type, mode);
1115 *bitsp = *bitsp & ~info->mask & ~info->bits;
1117 *bitsp = (*bitsp & ~info->mask) | info->bits;
1121 /* Combination mode */
1122 if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
1124 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1126 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
1127 } else if (info == &mode_info[IDX_oddp]) {
1129 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1131 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
1132 } else if (info == &mode_info[IDX_nl]) {
1134 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
1135 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
1137 mode->c_iflag = mode->c_iflag & ~ICRNL;
1138 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1140 } else if (info == &mode_info[IDX_ek]) {
1141 mode->c_cc[VERASE] = CERASE;
1142 mode->c_cc[VKILL] = CKILL;
1143 } else if (info == &mode_info[IDX_sane]) {
1145 } else if (info == &mode_info[IDX_cbreak]) {
1147 mode->c_lflag |= ICANON;
1149 mode->c_lflag &= ~ICANON;
1150 } else if (info == &mode_info[IDX_pass8]) {
1152 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1153 mode->c_iflag |= ISTRIP;
1155 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1156 mode->c_iflag &= ~ISTRIP;
1158 } else if (info == &mode_info[IDX_litout]) {
1160 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1161 mode->c_iflag |= ISTRIP;
1162 mode->c_oflag |= OPOST;
1164 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1165 mode->c_iflag &= ~ISTRIP;
1166 mode->c_oflag &= ~OPOST;
1168 } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
1169 if ((info == &mode_info[IDX_raw] && reversed)
1170 || (info == &mode_info[IDX_cooked] && !reversed)
1173 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1174 mode->c_oflag |= OPOST;
1175 mode->c_lflag |= ISIG | ICANON;
1177 mode->c_cc[VEOF] = CEOF;
1180 mode->c_cc[VEOL] = CEOL;
1185 mode->c_oflag &= ~OPOST;
1186 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1187 mode->c_cc[VMIN] = 1;
1188 mode->c_cc[VTIME] = 0;
1192 else if (info == &mode_info[IDX_decctlq]) {
1194 mode->c_iflag |= IXANY;
1196 mode->c_iflag &= ~IXANY;
1200 else if (info == &mode_info[IDX_tabs]) {
1202 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1204 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1208 else if (info == &mode_info[IDX_tabs]) {
1210 mode->c_oflag |= OXTABS;
1212 mode->c_oflag &= ~OXTABS;
1215 #if XCASE && IUCLC && OLCUC
1216 else if (info==&mode_info[IDX_lcase] || info==&mode_info[IDX_LCASE]) {
1218 mode->c_lflag &= ~XCASE;
1219 mode->c_iflag &= ~IUCLC;
1220 mode->c_oflag &= ~OLCUC;
1222 mode->c_lflag |= XCASE;
1223 mode->c_iflag |= IUCLC;
1224 mode->c_oflag |= OLCUC;
1228 else if (info == &mode_info[IDX_crt]) {
1229 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1230 } else if (info == &mode_info[IDX_dec]) {
1231 mode->c_cc[VINTR] = 3; /* ^C */
1232 mode->c_cc[VERASE] = 127; /* DEL */
1233 mode->c_cc[VKILL] = 21; /* ^U */
1234 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1235 if (IXANY) mode->c_iflag &= ~IXANY;
1239 static void set_control_char_or_die(const struct control_info *info,
1240 const char *arg, struct termios *mode)
1242 unsigned char value;
1244 if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
1245 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1246 else if (arg[0] == '\0' || arg[1] == '\0')
1248 else if (strcmp(arg, "^-") == 0 || strcmp(arg, "undef") == 0)
1249 value = _POSIX_VDISABLE;
1250 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1251 value = arg[1] & 0x1f; /* Non-letters get weird results */
1255 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1256 mode->c_cc[info->offset] = value;
1259 #define STTY_require_set_attr (1 << 0)
1260 #define STTY_speed_was_set (1 << 1)
1261 #define STTY_verbose_output (1 << 2)
1262 #define STTY_recoverable_output (1 << 3)
1263 #define STTY_noargs (1 << 4)
1265 int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1266 int stty_main(int argc UNUSED_PARAM, char **argv)
1268 struct termios mode;
1269 void (*output_func)(const struct termios *, int);
1270 const char *file_name = NULL;
1271 int display_all = 0;
1277 stty_state = STTY_noargs;
1278 output_func = do_display;
1280 /* First pass: only parse/verify command line params */
1283 const struct mode_info *mp;
1284 const struct control_info *cp;
1285 const char *arg = argv[k];
1286 const char *argnext = argv[k+1];
1289 if (arg[0] == '-') {
1291 mp = find_mode(arg+1);
1293 if (!(mp->flags & REV))
1294 goto invalid_argument;
1295 stty_state &= ~STTY_noargs;
1298 /* It is an option - parse it */
1303 stty_state |= STTY_verbose_output;
1304 output_func = do_display;
1308 stty_state |= STTY_recoverable_output;
1309 output_func = display_recoverable;
1313 bb_error_msg_and_die("only one device may be specified");
1314 file_name = &arg[i+1]; /* "-Fdevice" ? */
1315 if (!file_name[0]) { /* nope, "-F device" */
1316 int p = k+1; /* argv[p] is argnext */
1317 file_name = argnext;
1319 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1320 /* remove -F param from arg[vc] */
1322 argv[p] = argv[p+1];
1328 goto invalid_argument;
1335 mp = find_mode(arg);
1337 stty_state &= ~STTY_noargs;
1341 cp = find_control(arg);
1344 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1345 /* called for the side effect of xfunc death only */
1346 set_control_char_or_die(cp, argnext, &mode);
1347 stty_state &= ~STTY_noargs;
1352 param = find_param(arg);
1353 if (param & param_need_arg) {
1355 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1363 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1365 # endif /* else fall-through */
1371 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1378 /* called for the side effect of xfunc death only */
1379 set_speed_or_die(input_speed, argnext, &mode);
1382 /* called for the side effect of xfunc death only */
1383 set_speed_or_die(output_speed, argnext, &mode);
1386 if (recover_mode(arg, &mode) == 1) break;
1387 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1389 bb_error_msg_and_die("invalid argument '%s'", arg);
1391 stty_state &= ~STTY_noargs;
1394 /* Specifying both -a and -g is an error */
1395 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1396 (STTY_verbose_output | STTY_recoverable_output))
1397 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
1398 /* Specifying -a or -g with non-options is an error */
1399 if (!(stty_state & STTY_noargs)
1400 && (stty_state & (STTY_verbose_output | STTY_recoverable_output))
1402 bb_error_msg_and_die("modes may not be set when specifying an output style");
1405 /* Now it is safe to start doing things */
1407 G.device_name = file_name;
1408 xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO);
1409 ndelay_off(STDIN_FILENO);
1412 /* Initialize to all zeroes so there is no risk memcmp will report a
1413 spurious difference in an uninitialized portion of the structure */
1414 memset(&mode, 0, sizeof(mode));
1415 if (tcgetattr(STDIN_FILENO, &mode))
1416 perror_on_device_and_die("%s");
1418 if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
1419 get_terminal_width_height(STDOUT_FILENO, &G.max_col, NULL);
1420 output_func(&mode, display_all);
1421 return EXIT_SUCCESS;
1424 /* Second pass: perform actions */
1427 const struct mode_info *mp;
1428 const struct control_info *cp;
1429 const char *arg = argv[k];
1430 const char *argnext = argv[k+1];
1433 if (arg[0] == '-') {
1434 mp = find_mode(arg+1);
1436 set_mode(mp, 1 /* reversed */, &mode);
1437 stty_state |= STTY_require_set_attr;
1439 /* It is an option - already parsed. Skip it */
1443 mp = find_mode(arg);
1445 set_mode(mp, 0 /* non-reversed */, &mode);
1446 stty_state |= STTY_require_set_attr;
1450 cp = find_control(arg);
1453 set_control_char_or_die(cp, argnext, &mode);
1454 stty_state |= STTY_require_set_attr;
1458 param = find_param(arg);
1459 if (param & param_need_arg) {
1466 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1467 stty_state |= STTY_require_set_attr;
1473 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1476 display_window_size(0);
1479 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1483 display_speed(&mode, 0);
1486 set_speed_or_die(input_speed, argnext, &mode);
1487 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1490 set_speed_or_die(output_speed, argnext, &mode);
1491 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1494 if (recover_mode(arg, &mode) == 1)
1495 stty_state |= STTY_require_set_attr;
1496 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
1497 set_speed_or_die(both_speeds, arg, &mode);
1498 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1499 } /* else - impossible (caught in the first pass):
1500 bb_error_msg_and_die("invalid argument '%s'", arg); */
1504 if (stty_state & STTY_require_set_attr) {
1505 struct termios new_mode;
1507 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1508 perror_on_device_and_die("%s");
1510 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1511 it performs *any* of the requested operations. This means it
1512 can report 'success' when it has actually failed to perform
1513 some proper subset of the requested operations. To detect
1514 this partial failure, get the current terminal attributes and
1515 compare them to the requested ones */
1517 /* Initialize to all zeroes so there is no risk memcmp will report a
1518 spurious difference in an uninitialized portion of the structure */
1519 memset(&new_mode, 0, sizeof(new_mode));
1520 if (tcgetattr(STDIN_FILENO, &new_mode))
1521 perror_on_device_and_die("%s");
1523 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1525 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1526 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1527 sometimes (m1 != m2). The only difference is in the four bits
1528 of the c_cflag field corresponding to the baud rate. To save
1529 Sun users a little confusion, don't report an error if this
1530 happens. But suppress the error only if we haven't tried to
1531 set the baud rate explicitly -- otherwise we'd never give an
1532 error for a true failure to set the baud rate */
1534 new_mode.c_cflag &= (~CIBAUD);
1535 if ((stty_state & STTY_speed_was_set)
1536 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1538 perror_on_device_and_die("%s: cannot perform all requested operations");
1542 return EXIT_SUCCESS;