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 const char name[9]; /* Name given on command line */
139 const unsigned char type; /* Which structure element to change */
140 const unsigned char flags; /* Setting and display options */
141 /* were using short here, but ppc32 was unhappy: */
142 const tcflag_t mask; /* Other bits to turn off for this mode */
143 const tcflag_t bits; /* Bits to set for this mode */
147 /* Must match mode_info[] order! */
164 #if defined(TABDLY) || defined(OXTABS)
167 #if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
173 #define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B }
175 static const struct mode_info mode_info[] = {
176 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 ),
177 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 ),
178 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 ),
179 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ),
180 MI_ENTRY("ek", combination, OMIT, 0, 0 ),
181 MI_ENTRY("sane", combination, OMIT, 0, 0 ),
182 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ),
183 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ),
184 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ),
185 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ),
186 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ),
187 MI_ENTRY("crt", combination, OMIT, 0, 0 ),
188 MI_ENTRY("dec", combination, OMIT, 0, 0 ),
190 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 ),
192 #if defined(TABDLY) || defined(OXTABS)
193 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 ),
195 #if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
196 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 ),
197 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 ),
199 MI_ENTRY("parenb", control, REV, PARENB, 0 ),
200 MI_ENTRY("parodd", control, REV, PARODD, 0 ),
201 MI_ENTRY("cs5", control, 0, CS5, CSIZE),
202 MI_ENTRY("cs6", control, 0, CS6, CSIZE),
203 MI_ENTRY("cs7", control, 0, CS7, CSIZE),
204 MI_ENTRY("cs8", control, 0, CS8, CSIZE),
205 MI_ENTRY("hupcl", control, REV, HUPCL, 0 ),
206 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ),
207 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ),
208 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ),
209 MI_ENTRY("clocal", control, REV, CLOCAL, 0 ),
211 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ),
213 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ),
214 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ),
215 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ),
216 MI_ENTRY("parmrk", input, REV, PARMRK, 0 ),
217 MI_ENTRY("inpck", input, REV, INPCK, 0 ),
218 MI_ENTRY("istrip", input, REV, ISTRIP, 0 ),
219 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ),
220 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ),
221 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ),
222 MI_ENTRY("ixon", input, REV, IXON, 0 ),
223 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ),
224 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 ),
226 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ),
229 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ),
232 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ),
234 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ),
236 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ),
239 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ),
242 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ),
245 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ),
248 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ),
251 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ),
254 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ),
257 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY),
258 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY),
261 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY),
262 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY),
263 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY),
264 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY),
268 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY),
269 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY),
270 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY),
271 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY),
274 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ),
279 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY),
280 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY),
283 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY),
284 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY),
287 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY),
288 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY),
290 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ),
291 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ),
293 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ),
295 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ),
296 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ),
297 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 ),
298 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ),
299 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ),
300 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ),
302 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ),
305 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ),
308 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ),
309 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 ),
312 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ),
313 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 ),
316 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ),
317 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 ),
322 NUM_mode_info = ARRAY_SIZE(mode_info)
325 /* Control characters */
326 struct control_info {
327 const char name[7]; /* Name given on command line */
328 const unsigned char saneval; /* Value to set for 'stty sane' */
329 const unsigned char offset; /* Offset in c_cc */
333 /* Must match control_info[] order! */
371 static const struct control_info control_info[] = {
372 {"intr", CINTR, VINTR},
373 {"quit", CQUIT, VQUIT},
374 {"erase", CERASE, VERASE},
375 {"kill", CKILL, VKILL},
379 {"eol2", CEOL2, VEOL2},
382 {"swtch", CSWTCH, VSWTCH},
384 {"start", CSTART, VSTART},
385 {"stop", CSTOP, VSTOP},
386 {"susp", CSUSP, VSUSP},
388 {"dsusp", CDSUSP, VDSUSP},
391 {"rprnt", CRPRNT, VREPRINT},
394 {"werase", CWERASE, VWERASE},
397 {"lnext", CLNEXT, VLNEXT},
400 {"flush", CFLUSHO, VFLUSHO},
403 {"status", CSTATUS, VSTATUS},
405 /* These must be last because of the display routines */
411 NUM_control_info = ARRAY_SIZE(control_info)
414 /* The width of the screen, for output wrapping */
415 static unsigned max_col = 80; /* default */
418 /* Current position, to know when to wrap */
419 unsigned current_col;
422 #define G (*(struct globals*)&bb_common_bufsiz1)
424 static const char *device_name = bb_msg_standard_input;
426 /* Return a string that is the printable representation of character CH */
427 /* Adapted from 'cat' by Torbjorn Granlund */
428 static const char *visible(unsigned ch)
432 if (ch == _POSIX_VDISABLE)
444 } else if (ch < 127) {
455 static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
457 static const unsigned char tcflag_offsets[] ALIGN1 = {
458 offsetof(struct termios, c_cflag), /* control */
459 offsetof(struct termios, c_iflag), /* input */
460 offsetof(struct termios, c_oflag), /* output */
461 offsetof(struct termios, c_lflag) /* local */
465 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
470 static void set_speed_or_die(enum speed_setting type, const char *const arg,
471 struct termios * const mode)
475 baud = tty_value_to_baud(xatou(arg));
477 if (type != output_speed) { /* either input or both */
478 cfsetispeed(mode, baud);
480 if (type != input_speed) { /* either output or both */
481 cfsetospeed(mode, baud);
485 static ATTRIBUTE_NORETURN void perror_on_device_and_die(const char *fmt)
487 bb_perror_msg_and_die(fmt, device_name);
490 static void perror_on_device(const char *fmt)
492 bb_perror_msg(fmt, device_name);
495 /* Print format string MESSAGE and optional args.
496 Wrap to next line first if it won't fit.
497 Print a space first unless MESSAGE will start a new line */
498 static void wrapf(const char *message, ...)
504 va_start(args, message);
505 buflen = vsnprintf(buf, sizeof(buf), message, args);
507 /* We seem to be called only with suitable lengths, but check if
508 somebody failed to adhere to this assumption just to be sure. */
509 if (!buflen || buflen >= sizeof(buf)) return;
511 if (G.current_col > 0) {
513 if (buf[0] != '\n') {
514 if (G.current_col + buflen >= max_col) {
522 G.current_col += buflen;
523 if (buf[buflen-1] == '\n')
527 static void set_window_size(const int rows, const int cols)
529 struct winsize win = { 0, 0, 0, 0};
531 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
532 if (errno != EINVAL) {
535 memset(&win, 0, sizeof(win));
543 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
545 perror_on_device("%s");
548 static void display_window_size(const int fancy)
550 const char *fmt_str = "%s\0%s: no size information for this device";
551 unsigned width, height;
553 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
554 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
555 perror_on_device(fmt_str);
558 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
563 static const struct suffix_mult stty_suffixes[] = {
570 static const struct mode_info *find_mode(const char *name)
573 for (i = 0; i < NUM_mode_info; ++i)
574 if (!strcmp(name, mode_info[i].name))
575 return &mode_info[i];
579 static const struct control_info *find_control(const char *name)
582 for (i = 0; i < NUM_control_info; ++i)
583 if (!strcmp(name, control_info[i].name))
584 return &control_info[i];
589 param_need_arg = 0x80,
590 param_line = 1 | 0x80,
591 param_rows = 2 | 0x80,
592 param_cols = 3 | 0x80,
593 param_columns = 4 | 0x80,
596 param_ispeed = 7 | 0x80,
597 param_ospeed = 8 | 0x80,
600 static int find_param(const char *const name)
602 static const char params[] ALIGN1 =
611 int i = index_in_strings(params, name) + 1;
614 if (i != 5 && i != 6)
619 static int recover_mode(const char *arg, struct termios *mode)
623 unsigned long iflag, oflag, cflag, lflag;
625 /* Scan into temporaries since it is too much trouble to figure out
626 the right format for 'tcflag_t' */
627 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
628 &iflag, &oflag, &cflag, &lflag, &n) != 4)
630 mode->c_iflag = iflag;
631 mode->c_oflag = oflag;
632 mode->c_cflag = cflag;
633 mode->c_lflag = lflag;
635 for (i = 0; i < NCCS; ++i) {
636 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
642 /* Fail if there are too many fields */
649 static void display_recoverable(const struct termios *mode,
650 int ATTRIBUTE_UNUSED dummy)
653 printf("%lx:%lx:%lx:%lx",
654 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
655 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
656 for (i = 0; i < NCCS; ++i)
657 printf(":%x", (unsigned int) mode->c_cc[i]);
661 static void display_speed(const struct termios *mode, int fancy)
664 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
665 unsigned long ispeed, ospeed;
667 ospeed = ispeed = cfgetispeed(mode);
668 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
669 ispeed = ospeed; /* in case ispeed was 0 */
671 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
673 if (fancy) fmt_str += 9;
674 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
677 static void do_display(const struct termios *mode, const int all)
682 int prev_type = control;
684 display_speed(mode, 1);
686 display_window_size(1);
688 wrapf("line = %d;\n", mode->c_line);
693 for (i = 0; i != CIDX_min; ++i) {
694 /* If swtch is the same as susp, don't print both */
699 /* If eof uses the same slot as min, only print whichever applies */
701 if ((mode->c_lflag & ICANON) == 0
702 && (i == CIDX_eof || i == CIDX_eol)
707 wrapf("%s = %s;", control_info[i].name,
708 visible(mode->c_cc[control_info[i].offset]));
711 if ((mode->c_lflag & ICANON) == 0)
713 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
714 if (G.current_col) wrapf("\n");
716 for (i = 0; i < NUM_mode_info; ++i) {
717 if (mode_info[i].flags & OMIT)
719 if (mode_info[i].type != prev_type) {
721 if (G.current_col) wrapf("\n");
722 prev_type = mode_info[i].type;
725 bitsp = mode_type_flag(mode_info[i].type, mode);
726 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
727 if ((*bitsp & mask) == mode_info[i].bits) {
728 if (all || (mode_info[i].flags & SANE_UNSET))
729 wrapf("%s", mode_info[i].name);
731 if ((all && mode_info[i].flags & REV) ||
733 (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)))
734 wrapf("-%s", mode_info[i].name);
737 if (G.current_col) wrapf("\n");
740 static void sane_mode(struct termios *mode)
745 for (i = 0; i < NUM_control_info; ++i) {
750 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
753 for (i = 0; i < NUM_mode_info; ++i) {
754 if (mode_info[i].flags & SANE_SET) {
755 bitsp = mode_type_flag(mode_info[i].type, mode);
756 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
758 } else if (mode_info[i].flags & SANE_UNSET) {
759 bitsp = mode_type_flag(mode_info[i].type, mode);
760 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
761 & ~mode_info[i].bits;
766 /* Save set_mode from #ifdef forest plague */
801 static void set_mode(const struct mode_info *info, int reversed,
802 struct termios *mode)
806 bitsp = mode_type_flag(info->type, mode);
810 *bitsp = *bitsp & ~info->mask & ~info->bits;
812 *bitsp = (*bitsp & ~info->mask) | info->bits;
816 /* Combination mode */
817 if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
819 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
821 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
822 } else if (info == &mode_info[IDX_oddp]) {
824 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
826 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
827 } else if (info == &mode_info[IDX_nl]) {
829 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
830 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
832 mode->c_iflag = mode->c_iflag & ~ICRNL;
833 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
835 } else if (info == &mode_info[IDX_ek]) {
836 mode->c_cc[VERASE] = CERASE;
837 mode->c_cc[VKILL] = CKILL;
838 } else if (info == &mode_info[IDX_sane]) {
840 } else if (info == &mode_info[IDX_cbreak]) {
842 mode->c_lflag |= ICANON;
844 mode->c_lflag &= ~ICANON;
845 } else if (info == &mode_info[IDX_pass8]) {
847 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
848 mode->c_iflag |= ISTRIP;
850 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
851 mode->c_iflag &= ~ISTRIP;
853 } else if (info == &mode_info[IDX_litout]) {
855 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
856 mode->c_iflag |= ISTRIP;
857 mode->c_oflag |= OPOST;
859 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
860 mode->c_iflag &= ~ISTRIP;
861 mode->c_oflag &= ~OPOST;
863 } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
864 if ((info->name[0] == 'r' && reversed)
865 || (info->name[0] == 'c' && !reversed)
868 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
869 mode->c_oflag |= OPOST;
870 mode->c_lflag |= ISIG | ICANON;
872 mode->c_cc[VEOF] = CEOF;
875 mode->c_cc[VEOL] = CEOL;
880 mode->c_oflag &= ~OPOST;
881 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
882 mode->c_cc[VMIN] = 1;
883 mode->c_cc[VTIME] = 0;
886 else if (IXANY && info == &mode_info[IDX_decctlq]) {
888 mode->c_iflag |= IXANY;
890 mode->c_iflag &= ~IXANY;
892 else if (TABDLY && info == &mode_info[IDX_tabs]) {
894 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
896 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
898 else if (OXTABS && info == &mode_info[IDX_tabs]) {
900 mode->c_oflag |= OXTABS;
902 mode->c_oflag &= ~OXTABS;
904 if (XCASE && IUCLC && OLCUC
905 && (info == &mode_info[IDX_lcase] || info == &mode_info[IDX_LCASE])
908 mode->c_lflag &= ~XCASE;
909 mode->c_iflag &= ~IUCLC;
910 mode->c_oflag &= ~OLCUC;
912 mode->c_lflag |= XCASE;
913 mode->c_iflag |= IUCLC;
914 mode->c_oflag |= OLCUC;
916 } else if (info == &mode_info[IDX_crt]) {
917 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
918 } else if (info == &mode_info[IDX_dec]) {
919 mode->c_cc[VINTR] = 3; /* ^C */
920 mode->c_cc[VERASE] = 127; /* DEL */
921 mode->c_cc[VKILL] = 21; /* ^U */
922 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
923 if (IXANY) mode->c_iflag &= ~IXANY;
927 static void set_control_char_or_die(const struct control_info *info,
928 const char *arg, struct termios *mode)
932 if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
933 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
934 else if (arg[0] == '\0' || arg[1] == '\0')
936 else if (!strcmp(arg, "^-") || !strcmp(arg, "undef"))
937 value = _POSIX_VDISABLE;
938 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
939 value = arg[1] & 0x1f; /* Non-letters get weird results */
943 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
944 mode->c_cc[info->offset] = value;
947 #define STTY_require_set_attr (1<<0)
948 #define STTY_speed_was_set (1<<1)
949 #define STTY_verbose_output (1<<2)
950 #define STTY_recoverable_output (1<<3)
951 #define STTY_noargs (1<<4)
952 int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
953 int stty_main(int argc, char **argv)
956 void (*output_func)(const struct termios *, const int);
957 const char *file_name = NULL;
962 stty_state = STTY_noargs;
963 output_func = do_display;
965 /* First pass: only parse/verify command line params */
968 const struct mode_info *mp;
969 const struct control_info *cp;
970 const char *arg = argv[k];
971 const char *argnext = argv[k+1];
976 mp = find_mode(arg+1);
978 if (!(mp->flags & REV))
979 goto invalid_argument;
980 stty_state &= ~STTY_noargs;
983 /* It is an option - parse it */
988 stty_state |= STTY_verbose_output;
989 output_func = do_display;
993 stty_state |= STTY_recoverable_output;
994 output_func = display_recoverable;
998 bb_error_msg_and_die("only one device may be specified");
999 file_name = &arg[i+1]; /* "-Fdevice" ? */
1000 if (!file_name[0]) { /* nope, "-F device" */
1001 int p = k+1; /* argv[p] is argnext */
1002 file_name = argnext;
1004 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1005 /* remove -F param from arg[vc] */
1007 while (argv[p]) { argv[p] = argv[p+1]; ++p; }
1011 goto invalid_argument;
1018 mp = find_mode(arg);
1020 stty_state &= ~STTY_noargs;
1024 cp = find_control(arg);
1027 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1028 /* called for the side effect of xfunc death only */
1029 set_control_char_or_die(cp, argnext, &mode);
1030 stty_state &= ~STTY_noargs;
1035 param = find_param(arg);
1036 if (param & param_need_arg) {
1038 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1046 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1048 # endif /* else fall-through */
1054 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1061 /* called for the side effect of xfunc death only */
1062 set_speed_or_die(input_speed, argnext, &mode);
1065 /* called for the side effect of xfunc death only */
1066 set_speed_or_die(output_speed, argnext, &mode);
1069 if (recover_mode(arg, &mode) == 1) break;
1070 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1072 bb_error_msg_and_die("invalid argument '%s'", arg);
1074 stty_state &= ~STTY_noargs;
1077 /* Specifying both -a and -g is an error */
1078 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1079 (STTY_verbose_output | STTY_recoverable_output))
1080 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
1081 /* Specifying -a or -g with non-options is an error */
1082 if (!(stty_state & STTY_noargs) &&
1083 (stty_state & (STTY_verbose_output | STTY_recoverable_output)))
1084 bb_error_msg_and_die("modes may not be set when specifying an output style");
1086 /* Now it is safe to start doing things */
1089 device_name = file_name;
1090 fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
1091 if (fd != STDIN_FILENO) {
1092 dup2(fd, STDIN_FILENO);
1095 fdflags = fcntl(STDIN_FILENO, F_GETFL);
1097 fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
1098 perror_on_device_and_die("%s: cannot reset non-blocking mode");
1101 /* Initialize to all zeroes so there is no risk memcmp will report a
1102 spurious difference in an uninitialized portion of the structure */
1103 memset(&mode, 0, sizeof(mode));
1104 if (tcgetattr(STDIN_FILENO, &mode))
1105 perror_on_device_and_die("%s");
1107 if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
1108 get_terminal_width_height(STDOUT_FILENO, &max_col, NULL);
1109 output_func(&mode, display_all);
1110 return EXIT_SUCCESS;
1113 /* Second pass: perform actions */
1116 const struct mode_info *mp;
1117 const struct control_info *cp;
1118 const char *arg = argv[k];
1119 const char *argnext = argv[k+1];
1122 if (arg[0] == '-') {
1123 mp = find_mode(arg+1);
1125 set_mode(mp, 1 /* reversed */, &mode);
1126 stty_state |= STTY_require_set_attr;
1128 /* It is an option - already parsed. Skip it */
1132 mp = find_mode(arg);
1134 set_mode(mp, 0 /* non-reversed */, &mode);
1135 stty_state |= STTY_require_set_attr;
1139 cp = find_control(arg);
1142 set_control_char_or_die(cp, argnext, &mode);
1143 stty_state |= STTY_require_set_attr;
1147 param = find_param(arg);
1148 if (param & param_need_arg) {
1155 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1156 stty_state |= STTY_require_set_attr;
1161 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1164 display_window_size(0);
1167 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1171 display_speed(&mode, 0);
1174 set_speed_or_die(input_speed, argnext, &mode);
1175 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1178 set_speed_or_die(output_speed, argnext, &mode);
1179 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1182 if (recover_mode(arg, &mode) == 1)
1183 stty_state |= STTY_require_set_attr;
1184 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
1185 set_speed_or_die(both_speeds, arg, &mode);
1186 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1187 } /* else - impossible (caught in the first pass):
1188 bb_error_msg_and_die("invalid argument '%s'", arg); */
1192 if (stty_state & STTY_require_set_attr) {
1193 struct termios new_mode;
1195 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1196 perror_on_device_and_die("%s");
1198 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1199 it performs *any* of the requested operations. This means it
1200 can report 'success' when it has actually failed to perform
1201 some proper subset of the requested operations. To detect
1202 this partial failure, get the current terminal attributes and
1203 compare them to the requested ones */
1205 /* Initialize to all zeroes so there is no risk memcmp will report a
1206 spurious difference in an uninitialized portion of the structure */
1207 memset(&new_mode, 0, sizeof(new_mode));
1208 if (tcgetattr(STDIN_FILENO, &new_mode))
1209 perror_on_device_and_die("%s");
1211 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1213 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1214 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1215 sometimes (m1 != m2). The only difference is in the four bits
1216 of the c_cflag field corresponding to the baud rate. To save
1217 Sun users a little confusion, don't report an error if this
1218 happens. But suppress the error only if we haven't tried to
1219 set the baud rate explicitly -- otherwise we'd never give an
1220 error for a true failure to set the baud rate */
1222 new_mode.c_cflag &= (~CIBAUD);
1223 if ((stty_state & STTY_speed_was_set)
1224 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1226 perror_on_device_and_die("%s: cannot perform all requested operations");
1230 return EXIT_SUCCESS;