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 static const char evenp [] ALIGN1 = "evenp";
131 static const char raw [] ALIGN1 = "raw";
132 static const char stty_min [] ALIGN1 = "min";
133 static const char stty_time [] ALIGN1 = "time";
134 static const char stty_swtch[] ALIGN1 = "swtch";
135 static const char stty_eol [] ALIGN1 = "eol";
136 static const char stty_eof [] ALIGN1 = "eof";
137 static const char parity [] ALIGN1 = "parity";
138 static const char stty_oddp [] ALIGN1 = "oddp";
139 static const char stty_nl [] ALIGN1 = "nl";
140 static const char stty_ek [] ALIGN1 = "ek";
141 static const char stty_sane [] ALIGN1 = "sane";
142 static const char cbreak [] ALIGN1 = "cbreak";
143 static const char stty_pass8[] ALIGN1 = "pass8";
144 static const char litout [] ALIGN1 = "litout";
145 static const char cooked [] ALIGN1 = "cooked";
146 static const char decctlq [] ALIGN1 = "decctlq";
147 static const char stty_tabs [] ALIGN1 = "tabs";
148 static const char stty_lcase[] ALIGN1 = "lcase";
149 static const char stty_LCASE[] ALIGN1 = "LCASE";
150 static const char stty_crt [] ALIGN1 = "crt";
151 static const char stty_dec [] ALIGN1 = "dec";
153 /* Flags for 'struct mode_info' */
154 #define SANE_SET 1 /* Set in 'sane' mode */
155 #define SANE_UNSET 2 /* Unset in 'sane' mode */
156 #define REV 4 /* Can be turned off by prepending '-' */
157 #define OMIT 8 /* Don't display value */
161 const char *const name; /* Name given on command line */
162 const unsigned char type; /* Which structure element to change */
163 const unsigned char flags; /* Setting and display options */
164 /* were using short here, but ppc32 was unhappy: */
165 const tcflag_t mask; /* Other bits to turn off for this mode */
166 const tcflag_t bits; /* Bits to set for this mode */
169 /* We can optimize it further by using name[8] instead of char *name */
170 /* but beware of "if (info->name == evenp)" checks! */
171 /* Need to replace them with "if (info == &mode_info[EVENP_INDX])" */
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("parenb", control, REV, PARENB, 0 ),
177 MI_ENTRY("parodd", control, REV, PARODD, 0 ),
178 MI_ENTRY("cs5", control, 0, CS5, CSIZE),
179 MI_ENTRY("cs6", control, 0, CS6, CSIZE),
180 MI_ENTRY("cs7", control, 0, CS7, CSIZE),
181 MI_ENTRY("cs8", control, 0, CS8, CSIZE),
182 MI_ENTRY("hupcl", control, REV, HUPCL, 0 ),
183 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ),
184 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ),
185 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ),
186 MI_ENTRY("clocal", control, REV, CLOCAL, 0 ),
188 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ),
190 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ),
191 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ),
192 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ),
193 MI_ENTRY("parmrk", input, REV, PARMRK, 0 ),
194 MI_ENTRY("inpck", input, REV, INPCK, 0 ),
195 MI_ENTRY("istrip", input, REV, ISTRIP, 0 ),
196 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ),
197 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ),
198 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ),
199 MI_ENTRY("ixon", input, REV, IXON, 0 ),
200 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ),
201 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 ),
203 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ),
206 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ),
209 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ),
211 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ),
213 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ),
216 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ),
219 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ),
222 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ),
225 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ),
228 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ),
231 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ),
234 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY),
235 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY),
238 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY),
239 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY),
240 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY),
241 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY),
245 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY),
246 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY),
247 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY),
248 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY),
251 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ),
256 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY),
257 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY),
260 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY),
261 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY),
264 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY),
265 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY),
267 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ),
268 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ),
270 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ),
272 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ),
273 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ),
274 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 ),
275 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ),
276 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ),
277 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ),
279 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ),
282 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ),
285 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ),
286 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 ),
289 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ),
290 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 ),
293 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ),
294 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 ),
296 MI_ENTRY(evenp, combination, REV | OMIT, 0, 0 ),
297 MI_ENTRY(parity, combination, REV | OMIT, 0, 0 ),
298 MI_ENTRY(stty_oddp, combination, REV | OMIT, 0, 0 ),
299 MI_ENTRY(stty_nl, combination, REV | OMIT, 0, 0 ),
300 MI_ENTRY(stty_ek, combination, OMIT, 0, 0 ),
301 MI_ENTRY(stty_sane, combination, OMIT, 0, 0 ),
302 MI_ENTRY(cooked, combination, REV | OMIT, 0, 0 ),
303 MI_ENTRY(raw, combination, REV | OMIT, 0, 0 ),
304 MI_ENTRY(stty_pass8, combination, REV | OMIT, 0, 0 ),
305 MI_ENTRY(litout, combination, REV | OMIT, 0, 0 ),
306 MI_ENTRY(cbreak, combination, REV | OMIT, 0, 0 ),
308 MI_ENTRY(decctlq, combination, REV | OMIT, 0, 0 ),
310 #if defined(TABDLY) || defined(OXTABS)
311 MI_ENTRY(stty_tabs, combination, REV | OMIT, 0, 0 ),
313 #if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
314 MI_ENTRY(stty_lcase, combination, REV | OMIT, 0, 0 ),
315 MI_ENTRY(stty_LCASE, combination, REV | OMIT, 0, 0 ),
317 MI_ENTRY(stty_crt, combination, OMIT, 0, 0 ),
318 MI_ENTRY(stty_dec, combination, OMIT, 0, 0 ),
322 NUM_mode_info = ARRAY_SIZE(mode_info)
325 /* Control character settings */
326 struct control_info {
327 const char *const name; /* 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 */
332 /* Control characters */
334 static const struct control_info control_info[] = {
335 {"intr", CINTR, VINTR},
336 {"quit", CQUIT, VQUIT},
337 {"erase", CERASE, VERASE},
338 {"kill", CKILL, VKILL},
339 {stty_eof, CEOF, VEOF},
340 {stty_eol, CEOL, VEOL},
342 {"eol2", CEOL2, VEOL2},
345 {stty_swtch, CSWTCH, VSWTCH},
347 {"start", CSTART, VSTART},
348 {"stop", CSTOP, VSTOP},
349 {"susp", CSUSP, VSUSP},
351 {"dsusp", CDSUSP, VDSUSP},
354 {"rprnt", CRPRNT, VREPRINT},
357 {"werase", CWERASE, VWERASE},
360 {"lnext", CLNEXT, VLNEXT},
363 {"flush", CFLUSHO, VFLUSHO},
366 {"status", CSTATUS, VSTATUS},
368 /* These must be last because of the display routines */
370 {stty_time, 0, VTIME},
374 NUM_control_info = ARRAY_SIZE(control_info)
377 /* The width of the screen, for output wrapping */
378 unsigned max_col = 80; /* default */
381 /* Current position, to know when to wrap */
382 unsigned current_col;
385 #define G (*(struct globals*)&bb_common_bufsiz1)
387 static const char *device_name = bb_msg_standard_input;
389 /* Return a string that is the printable representation of character CH */
390 /* Adapted from 'cat' by Torbjorn Granlund */
391 static const char *visible(unsigned ch)
395 if (ch == _POSIX_VDISABLE)
407 } else if (ch < 127) {
418 static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
420 static const unsigned char tcflag_offsets[] ALIGN1 = {
421 offsetof(struct termios, c_cflag), /* control */
422 offsetof(struct termios, c_iflag), /* input */
423 offsetof(struct termios, c_oflag), /* output */
424 offsetof(struct termios, c_lflag) /* local */
428 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
433 static void set_speed_or_die(enum speed_setting type, const char *const arg,
434 struct termios * const mode)
438 baud = tty_value_to_baud(xatou(arg));
440 if (type != output_speed) { /* either input or both */
441 cfsetispeed(mode, baud);
443 if (type != input_speed) { /* either output or both */
444 cfsetospeed(mode, baud);
448 static ATTRIBUTE_NORETURN void perror_on_device_and_die(const char *fmt)
450 bb_perror_msg_and_die(fmt, device_name);
453 static void perror_on_device(const char *fmt)
455 bb_perror_msg(fmt, device_name);
458 /* Print format string MESSAGE and optional args.
459 Wrap to next line first if it won't fit.
460 Print a space first unless MESSAGE will start a new line */
461 static void wrapf(const char *message, ...)
467 va_start(args, message);
468 buflen = vsnprintf(buf, sizeof(buf), message, args);
470 /* We seem to be called only with suitable lengths, but check if
471 somebody failed to adhere to this assumption just to be sure. */
472 if (!buflen || buflen >= sizeof(buf)) return;
474 if (G.current_col > 0) {
476 if (buf[0] != '\n') {
477 if (G.current_col + buflen >= max_col) {
485 G.current_col += buflen;
486 if (buf[buflen-1] == '\n')
490 static void set_window_size(const int rows, const int cols)
492 struct winsize win = { 0, 0, 0, 0};
494 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
495 if (errno != EINVAL) {
498 memset(&win, 0, sizeof(win));
506 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
508 perror_on_device("%s");
511 static void display_window_size(const int fancy)
513 const char *fmt_str = "%s\0%s: no size information for this device";
514 unsigned width, height;
516 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
517 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
518 perror_on_device(fmt_str);
521 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
526 static const struct suffix_mult stty_suffixes[] = {
533 static const struct mode_info *find_mode(const char *name)
536 for (i = 0; i < NUM_mode_info; ++i)
537 if (!strcmp(name, mode_info[i].name))
538 return &mode_info[i];
542 static const struct control_info *find_control(const char *name)
545 for (i = 0; i < NUM_control_info; ++i)
546 if (!strcmp(name, control_info[i].name))
547 return &control_info[i];
552 param_need_arg = 0x80,
553 param_line = 1 | 0x80,
554 param_rows = 2 | 0x80,
555 param_cols = 3 | 0x80,
556 param_columns = 4 | 0x80,
559 param_ispeed = 7 | 0x80,
560 param_ospeed = 8 | 0x80,
563 static int find_param(const char *const name)
565 static const char params[] ALIGN1 =
574 int i = index_in_strings(params, name) + 1;
577 if (i != 5 && i != 6)
582 static int recover_mode(const char *arg, struct termios *mode)
586 unsigned long iflag, oflag, cflag, lflag;
588 /* Scan into temporaries since it is too much trouble to figure out
589 the right format for 'tcflag_t' */
590 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
591 &iflag, &oflag, &cflag, &lflag, &n) != 4)
593 mode->c_iflag = iflag;
594 mode->c_oflag = oflag;
595 mode->c_cflag = cflag;
596 mode->c_lflag = lflag;
598 for (i = 0; i < NCCS; ++i) {
599 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
605 /* Fail if there are too many fields */
612 static void display_recoverable(const struct termios *mode,
613 int ATTRIBUTE_UNUSED dummy)
616 printf("%lx:%lx:%lx:%lx",
617 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
618 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
619 for (i = 0; i < NCCS; ++i)
620 printf(":%x", (unsigned int) mode->c_cc[i]);
624 static void display_speed(const struct termios *mode, int fancy)
627 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
628 unsigned long ispeed, ospeed;
630 ospeed = ispeed = cfgetispeed(mode);
631 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
632 ispeed = ospeed; /* in case ispeed was 0 */
634 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
636 if (fancy) fmt_str += 9;
637 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
640 static void do_display(const struct termios *mode, const int all)
645 int prev_type = control;
647 display_speed(mode, 1);
649 display_window_size(1);
651 wrapf("line = %d;\n", mode->c_line);
656 for (i = 0; control_info[i].name != stty_min; ++i) {
657 /* If swtch is the same as susp, don't print both */
659 if (control_info[i].name == stty_swtch)
662 /* If eof uses the same slot as min, only print whichever applies */
664 if ((mode->c_lflag & ICANON) == 0
665 && (control_info[i].name == stty_eof
666 || control_info[i].name == stty_eol)) continue;
668 wrapf("%s = %s;", control_info[i].name,
669 visible(mode->c_cc[control_info[i].offset]));
672 if ((mode->c_lflag & ICANON) == 0)
674 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
675 if (G.current_col) wrapf("\n");
677 for (i = 0; i < NUM_mode_info; ++i) {
678 if (mode_info[i].flags & OMIT)
680 if (mode_info[i].type != prev_type) {
682 if (G.current_col) wrapf("\n");
683 prev_type = mode_info[i].type;
686 bitsp = mode_type_flag(mode_info[i].type, mode);
687 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
688 if ((*bitsp & mask) == mode_info[i].bits) {
689 if (all || (mode_info[i].flags & SANE_UNSET))
690 wrapf("%s", mode_info[i].name);
692 if ((all && mode_info[i].flags & REV) ||
694 (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)))
695 wrapf("-%s", mode_info[i].name);
698 if (G.current_col) wrapf("\n");
701 static void sane_mode(struct termios *mode)
706 for (i = 0; i < NUM_control_info; ++i) {
708 if (control_info[i].name == stty_min)
711 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
714 for (i = 0; i < NUM_mode_info; ++i) {
715 if (mode_info[i].flags & SANE_SET) {
716 bitsp = mode_type_flag(mode_info[i].type, mode);
717 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
719 } else if (mode_info[i].flags & SANE_UNSET) {
720 bitsp = mode_type_flag(mode_info[i].type, mode);
721 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
722 & ~mode_info[i].bits;
727 /* Save set_mode from #ifdef forest plague */
762 static void set_mode(const struct mode_info *info, int reversed,
763 struct termios *mode)
767 bitsp = mode_type_flag(info->type, mode);
771 *bitsp = *bitsp & ~info->mask & ~info->bits;
773 *bitsp = (*bitsp & ~info->mask) | info->bits;
777 /* Combination mode */
778 if (info->name == evenp || info->name == parity) {
780 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
782 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
783 } else if (info->name == stty_oddp) {
785 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
787 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
788 } else if (info->name == stty_nl) {
790 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
791 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
793 mode->c_iflag = mode->c_iflag & ~ICRNL;
794 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
796 } else if (info->name == stty_ek) {
797 mode->c_cc[VERASE] = CERASE;
798 mode->c_cc[VKILL] = CKILL;
799 } else if (info->name == stty_sane) {
802 else if (info->name == cbreak) {
804 mode->c_lflag |= ICANON;
806 mode->c_lflag &= ~ICANON;
807 } else if (info->name == stty_pass8) {
809 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
810 mode->c_iflag |= ISTRIP;
812 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
813 mode->c_iflag &= ~ISTRIP;
815 } else if (info->name == litout) {
817 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
818 mode->c_iflag |= ISTRIP;
819 mode->c_oflag |= OPOST;
821 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
822 mode->c_iflag &= ~ISTRIP;
823 mode->c_oflag &= ~OPOST;
825 } else if (info->name == raw || info->name == cooked) {
826 if ((info->name[0] == 'r' && reversed)
827 || (info->name[0] == 'c' && !reversed)) {
829 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
830 mode->c_oflag |= OPOST;
831 mode->c_lflag |= ISIG | ICANON;
833 mode->c_cc[VEOF] = CEOF;
836 mode->c_cc[VEOL] = CEOL;
841 mode->c_oflag &= ~OPOST;
842 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
843 mode->c_cc[VMIN] = 1;
844 mode->c_cc[VTIME] = 0;
847 else if (IXANY && info->name == decctlq) {
849 mode->c_iflag |= IXANY;
851 mode->c_iflag &= ~IXANY;
853 else if (TABDLY && info->name == stty_tabs) {
855 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
857 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
859 else if (OXTABS && info->name == stty_tabs) {
861 mode->c_oflag |= OXTABS;
863 mode->c_oflag &= ~OXTABS;
865 else if (XCASE && IUCLC && OLCUC
866 && (info->name == stty_lcase || info->name == stty_LCASE)) {
868 mode->c_lflag &= ~XCASE;
869 mode->c_iflag &= ~IUCLC;
870 mode->c_oflag &= ~OLCUC;
872 mode->c_lflag |= XCASE;
873 mode->c_iflag |= IUCLC;
874 mode->c_oflag |= OLCUC;
877 else if (info->name == stty_crt) {
878 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
880 else if (info->name == stty_dec) {
881 mode->c_cc[VINTR] = 3; /* ^C */
882 mode->c_cc[VERASE] = 127; /* DEL */
883 mode->c_cc[VKILL] = 21; /* ^U */
884 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
885 if (IXANY) mode->c_iflag &= ~IXANY;
889 static void set_control_char_or_die(const struct control_info *info,
890 const char *arg, struct termios *mode)
894 if (info->name == stty_min || info->name == stty_time)
895 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
896 else if (arg[0] == '\0' || arg[1] == '\0')
898 else if (!strcmp(arg, "^-") || !strcmp(arg, "undef"))
899 value = _POSIX_VDISABLE;
900 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
901 value = arg[1] & 0x1f; /* Non-letters get weird results */
905 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
906 mode->c_cc[info->offset] = value;
909 #define STTY_require_set_attr (1<<0)
910 #define STTY_speed_was_set (1<<1)
911 #define STTY_verbose_output (1<<2)
912 #define STTY_recoverable_output (1<<3)
913 #define STTY_noargs (1<<4)
914 int stty_main(int argc, char **argv);
915 int stty_main(int argc, char **argv)
918 void (*output_func)(const struct termios *, const int);
919 const char *file_name = NULL;
924 stty_state = STTY_noargs;
925 output_func = do_display;
927 /* First pass: only parse/verify command line params */
930 const struct mode_info *mp;
931 const struct control_info *cp;
932 const char *arg = argv[k];
933 const char *argnext = argv[k+1];
938 mp = find_mode(arg+1);
940 if (!(mp->flags & REV))
941 goto invalid_argument;
942 stty_state &= ~STTY_noargs;
945 /* It is an option - parse it */
950 stty_state |= STTY_verbose_output;
951 output_func = do_display;
955 stty_state |= STTY_recoverable_output;
956 output_func = display_recoverable;
960 bb_error_msg_and_die("only one device may be specified");
961 file_name = &arg[i+1]; /* "-Fdevice" ? */
962 if (!file_name[0]) { /* nope, "-F device" */
963 int p = k+1; /* argv[p] is argnext */
966 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
967 /* remove -F param from arg[vc] */
969 while (argv[p]) { argv[p] = argv[p+1]; ++p; }
973 goto invalid_argument;
982 stty_state &= ~STTY_noargs;
986 cp = find_control(arg);
989 bb_error_msg_and_die(bb_msg_requires_arg, arg);
990 /* called for the side effect of xfunc death only */
991 set_control_char_or_die(cp, argnext, &mode);
992 stty_state &= ~STTY_noargs;
997 param = find_param(arg);
998 if (param & param_need_arg) {
1000 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1008 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1010 # endif /* else fall-through */
1016 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1023 /* called for the side effect of xfunc death only */
1024 set_speed_or_die(input_speed, argnext, &mode);
1027 /* called for the side effect of xfunc death only */
1028 set_speed_or_die(output_speed, argnext, &mode);
1031 if (recover_mode(arg, &mode) == 1) break;
1032 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1034 bb_error_msg_and_die("invalid argument '%s'", arg);
1036 stty_state &= ~STTY_noargs;
1039 /* Specifying both -a and -g is an error */
1040 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1041 (STTY_verbose_output | STTY_recoverable_output))
1042 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
1043 /* Specifying -a or -g with non-options is an error */
1044 if (!(stty_state & STTY_noargs) &&
1045 (stty_state & (STTY_verbose_output | STTY_recoverable_output)))
1046 bb_error_msg_and_die("modes may not be set when specifying an output style");
1048 /* Now it is safe to start doing things */
1051 device_name = file_name;
1052 fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
1053 if (fd != STDIN_FILENO) {
1054 dup2(fd, STDIN_FILENO);
1057 fdflags = fcntl(STDIN_FILENO, F_GETFL);
1059 fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
1060 perror_on_device_and_die("%s: cannot reset non-blocking mode");
1063 /* Initialize to all zeroes so there is no risk memcmp will report a
1064 spurious difference in an uninitialized portion of the structure */
1065 memset(&mode, 0, sizeof(mode));
1066 if (tcgetattr(STDIN_FILENO, &mode))
1067 perror_on_device_and_die("%s");
1069 if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
1070 get_terminal_width_height(STDOUT_FILENO, &max_col, NULL);
1071 output_func(&mode, display_all);
1072 return EXIT_SUCCESS;
1075 /* Second pass: perform actions */
1078 const struct mode_info *mp;
1079 const struct control_info *cp;
1080 const char *arg = argv[k];
1081 const char *argnext = argv[k+1];
1084 if (arg[0] == '-') {
1085 mp = find_mode(arg+1);
1087 set_mode(mp, 1 /* reversed */, &mode);
1088 stty_state |= STTY_require_set_attr;
1090 /* It is an option - already parsed. Skip it */
1094 mp = find_mode(arg);
1096 set_mode(mp, 0 /* non-reversed */, &mode);
1097 stty_state |= STTY_require_set_attr;
1101 cp = find_control(arg);
1104 set_control_char_or_die(cp, argnext, &mode);
1105 stty_state |= STTY_require_set_attr;
1109 param = find_param(arg);
1110 if (param & param_need_arg) {
1117 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1118 stty_state |= STTY_require_set_attr;
1123 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1126 display_window_size(0);
1129 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1133 display_speed(&mode, 0);
1136 set_speed_or_die(input_speed, argnext, &mode);
1137 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1140 set_speed_or_die(output_speed, argnext, &mode);
1141 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1144 if (recover_mode(arg, &mode) == 1)
1145 stty_state |= STTY_require_set_attr;
1146 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
1147 set_speed_or_die(both_speeds, arg, &mode);
1148 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1149 } /* else - impossible (caught in the first pass):
1150 bb_error_msg_and_die("invalid argument '%s'", arg); */
1154 if (stty_state & STTY_require_set_attr) {
1155 struct termios new_mode;
1157 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1158 perror_on_device_and_die("%s");
1160 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1161 it performs *any* of the requested operations. This means it
1162 can report 'success' when it has actually failed to perform
1163 some proper subset of the requested operations. To detect
1164 this partial failure, get the current terminal attributes and
1165 compare them to the requested ones */
1167 /* Initialize to all zeroes so there is no risk memcmp will report a
1168 spurious difference in an uninitialized portion of the structure */
1169 memset(&new_mode, 0, sizeof(new_mode));
1170 if (tcgetattr(STDIN_FILENO, &new_mode))
1171 perror_on_device_and_die("%s");
1173 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1175 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1176 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1177 sometimes (m1 != m2). The only difference is in the four bits
1178 of the c_cflag field corresponding to the baud rate. To save
1179 Sun users a little confusion, don't report an error if this
1180 happens. But suppress the error only if we haven't tried to
1181 set the baud rate explicitly -- otherwise we'd never give an
1182 error for a true failure to set the baud rate */
1184 new_mode.c_cflag &= (~CIBAUD);
1185 if ((stty_state & STTY_speed_was_set)
1186 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1188 perror_on_device_and_die("%s: cannot perform all requested operations");
1192 return EXIT_SUCCESS;