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 [] = "evenp";
131 static const char raw [] = "raw";
132 static const char stty_min [] = "min";
133 static const char stty_time [] = "time";
134 static const char stty_swtch[] = "swtch";
135 static const char stty_eol [] = "eol";
136 static const char stty_eof [] = "eof";
137 static const char parity [] = "parity";
138 static const char stty_oddp [] = "oddp";
139 static const char stty_nl [] = "nl";
140 static const char stty_ek [] = "ek";
141 static const char stty_sane [] = "sane";
142 static const char cbreak [] = "cbreak";
143 static const char stty_pass8[] = "pass8";
144 static const char litout [] = "litout";
145 static const char cooked [] = "cooked";
146 static const char decctlq [] = "decctlq";
147 static const char stty_tabs [] = "tabs";
148 static const char stty_lcase[] = "lcase";
149 static const char stty_LCASE[] = "LCASE";
150 static const char stty_crt [] = "crt";
151 static const char stty_dec [] = "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 *name; /* Name given on command line */
162 char type; /* Which structure element to change */
163 char flags; /* Setting and display options */
164 /* were using short here, but ppc32 was unhappy: */
165 tcflag_t mask; /* Other bits to turn off for this mode */
166 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 = (sizeof(mode_info) / sizeof(mode_info[0]))
325 /* Control character settings */
326 struct control_info {
327 const char *name; /* Name given on command line */
328 unsigned char saneval; /* Value to set for 'stty sane' */
329 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 = (sizeof(control_info) / sizeof(control_info[0]))
377 /* The width of the screen, for output wrapping */
378 static unsigned max_col = 80; /* default */
379 /* Current position, to know when to wrap */
380 static unsigned current_col;
381 static const char *device_name = bb_msg_standard_input;
383 /* Return a string that is the printable representation of character CH */
384 /* Adapted from 'cat' by Torbjorn Granlund */
385 static const char *visible(unsigned int ch)
390 if (ch == _POSIX_VDISABLE)
402 } else if (ch < 127) {
413 static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
415 static const unsigned char tcflag_offsets[] = {
416 offsetof(struct termios, c_cflag), /* control */
417 offsetof(struct termios, c_iflag), /* input */
418 offsetof(struct termios, c_oflag), /* output */
419 offsetof(struct termios, c_lflag), /* local */
423 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
428 static speed_t string_to_baud_or_die(const char *arg)
430 return tty_value_to_baud(xatou(arg));
433 static void set_speed_or_die(enum speed_setting type, const char *arg,
434 struct termios *mode)
438 baud = string_to_baud_or_die(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 /* No, inline won't be as efficient (gcc 3.4.3) */
459 #define streq(a,b) (!strcmp((a),(b)))
461 /* Print format string MESSAGE and optional args.
462 Wrap to next line first if it won't fit.
463 Print a space first unless MESSAGE will start a new line */
464 static void wrapf(const char *message, ...)
470 va_start(args, message);
471 vsnprintf(buf, sizeof(buf), message, args);
473 buflen = strlen(buf);
476 if (current_col > 0) {
478 if (buf[0] != '\n') {
479 if (current_col + buflen >= max_col) {
487 current_col += buflen;
488 if (buf[buflen-1] == '\n')
494 static int get_win_size(int fd, struct winsize *win)
496 return ioctl(fd, TIOCGWINSZ, (char *) win);
499 static void set_window_size(int rows, int cols)
503 if (get_win_size(STDIN_FILENO, &win)) {
504 if (errno != EINVAL) {
505 perror_on_device("%s");
508 memset(&win, 0, sizeof(win));
517 /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
518 The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
519 This comment from sys/ttold.h describes Sun's twisted logic - a better
520 test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
521 At any rate, the problem is gone in Solaris 2.x */
523 if (win.ws_row == 0 || win.ws_col == 0) {
524 struct ttysize ttysz;
526 ttysz.ts_lines = win.ws_row;
527 ttysz.ts_cols = win.ws_col;
529 win.ws_row = win.ws_col = 1;
531 if ((ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win) != 0)
532 || (ioctl(STDIN_FILENO, TIOCSSIZE, (char *) &ttysz) != 0)) {
533 perror_on_device("%s");
539 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
540 perror_on_device("%s");
543 static void display_window_size(int fancy)
545 const char *fmt_str = "%s\0%s: no size information for this device";
548 if (get_win_size(STDIN_FILENO, &win)) {
549 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
550 perror_on_device(fmt_str);
553 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
554 win.ws_row, win.ws_col);
558 #else /* !TIOCGWINSZ */
560 static inline void display_window_size(int fancy) {}
562 #endif /* !TIOCGWINSZ */
564 static int screen_columns_or_die(void)
571 /* With Solaris 2.[123], this ioctl fails and errno is set to
572 EINVAL for telnet (but not rlogin) sessions.
573 On ISC 3.0, it fails for the console and the serial port
574 (but it works for ptys).
575 It can also fail on any system when stdout isn't a tty.
576 In case of any failure, just use the default */
577 if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0)
581 s = getenv("COLUMNS");
587 static const struct suffix_mult stty_suffixes[] = {
594 static const struct mode_info *find_mode(const char *name)
597 for (i = 0; i < NUM_mode_info; ++i)
598 if (streq(name, mode_info[i].name))
599 return &mode_info[i];
603 static const struct control_info *find_control(const char *name)
606 for (i = 0; i < NUM_control_info; ++i)
607 if (streq(name, control_info[i].name))
608 return &control_info[i];
613 param_need_arg = 0x80,
614 param_line = 1 | 0x80,
615 param_rows = 2 | 0x80,
616 param_cols = 3 | 0x80,
619 param_ispeed = 6 | 0x80,
620 param_ospeed = 7 | 0x80,
623 static int find_param(const char *name)
626 if (streq(name, "line")) return param_line;
629 if (streq(name, "rows")) return param_rows;
630 if (streq(name, "cols")) return param_cols;
631 if (streq(name, "columns")) return param_cols;
632 if (streq(name, "size")) return param_size;
634 if (streq(name, "speed")) return param_speed;
635 if (streq(name, "ispeed")) return param_ispeed;
636 if (streq(name, "ospeed")) return param_ospeed;
640 static int recover_mode(const char *arg, struct termios *mode)
644 unsigned long iflag, oflag, cflag, lflag;
646 /* Scan into temporaries since it is too much trouble to figure out
647 the right format for 'tcflag_t' */
648 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
649 &iflag, &oflag, &cflag, &lflag, &n) != 4)
651 mode->c_iflag = iflag;
652 mode->c_oflag = oflag;
653 mode->c_cflag = cflag;
654 mode->c_lflag = lflag;
656 for (i = 0; i < NCCS; ++i) {
657 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
663 /* Fail if there are too many fields */
670 static void display_recoverable(const struct termios *mode)
673 printf("%lx:%lx:%lx:%lx",
674 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
675 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
676 for (i = 0; i < NCCS; ++i)
677 printf(":%x", (unsigned int) mode->c_cc[i]);
681 static void display_speed(const struct termios *mode, int fancy)
684 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
685 unsigned long ispeed, ospeed;
687 ospeed = ispeed = cfgetispeed(mode);
688 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
689 ispeed = ospeed; /* in case ispeed was 0 */
691 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
693 if (fancy) fmt_str += 9;
694 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
697 static void display_all(const struct termios *mode)
702 int prev_type = control;
704 display_speed(mode, 1);
705 display_window_size(1);
707 wrapf("line = %d;\n", mode->c_line);
712 for (i = 0; control_info[i].name != stty_min; ++i) {
713 /* If swtch is the same as susp, don't print both */
715 if (control_info[i].name == stty_swtch)
718 /* If eof uses the same slot as min, only print whichever applies */
720 if ((mode->c_lflag & ICANON) == 0
721 && (control_info[i].name == stty_eof
722 || control_info[i].name == stty_eol)) continue;
724 wrapf("%s = %s;", control_info[i].name,
725 visible(mode->c_cc[control_info[i].offset]));
728 if ((mode->c_lflag & ICANON) == 0)
730 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
731 if (current_col) wrapf("\n");
733 for (i = 0; i < NUM_mode_info; ++i) {
734 if (mode_info[i].flags & OMIT)
736 if (mode_info[i].type != prev_type) {
738 prev_type = mode_info[i].type;
741 bitsp = mode_type_flag(mode_info[i].type, mode);
742 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
743 if ((*bitsp & mask) == mode_info[i].bits)
744 wrapf("%s", mode_info[i].name);
745 else if (mode_info[i].flags & REV)
746 wrapf("-%s", mode_info[i].name);
748 if (current_col) wrapf("\n");
751 static void sane_mode(struct termios *mode)
756 for (i = 0; i < NUM_control_info; ++i) {
758 if (control_info[i].name == stty_min)
761 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
764 for (i = 0; i < NUM_mode_info; ++i) {
765 if (mode_info[i].flags & SANE_SET) {
766 bitsp = mode_type_flag(mode_info[i].type, mode);
767 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
769 } else if (mode_info[i].flags & SANE_UNSET) {
770 bitsp = mode_type_flag(mode_info[i].type, mode);
771 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
772 & ~mode_info[i].bits;
777 /* Save set_mode from #ifdef forest plague */
812 static void set_mode(const struct mode_info *info, int reversed,
813 struct termios *mode)
817 bitsp = mode_type_flag(info->type, mode);
821 *bitsp = *bitsp & ~info->mask & ~info->bits;
823 *bitsp = (*bitsp & ~info->mask) | info->bits;
827 /* Combination mode */
828 if (info->name == evenp || info->name == parity) {
830 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
832 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
833 } else if (info->name == stty_oddp) {
835 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
837 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
838 } else if (info->name == stty_nl) {
840 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
841 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
843 mode->c_iflag = mode->c_iflag & ~ICRNL;
844 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
846 } else if (info->name == stty_ek) {
847 mode->c_cc[VERASE] = CERASE;
848 mode->c_cc[VKILL] = CKILL;
849 } else if (info->name == stty_sane) {
852 else if (info->name == cbreak) {
854 mode->c_lflag |= ICANON;
856 mode->c_lflag &= ~ICANON;
857 } else if (info->name == stty_pass8) {
859 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
860 mode->c_iflag |= ISTRIP;
862 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
863 mode->c_iflag &= ~ISTRIP;
865 } else if (info->name == litout) {
867 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
868 mode->c_iflag |= ISTRIP;
869 mode->c_oflag |= OPOST;
871 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
872 mode->c_iflag &= ~ISTRIP;
873 mode->c_oflag &= ~OPOST;
875 } else if (info->name == raw || info->name == cooked) {
876 if ((info->name[0] == 'r' && reversed)
877 || (info->name[0] == 'c' && !reversed)) {
879 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
880 mode->c_oflag |= OPOST;
881 mode->c_lflag |= ISIG | ICANON;
883 mode->c_cc[VEOF] = CEOF;
886 mode->c_cc[VEOL] = CEOL;
891 mode->c_oflag &= ~OPOST;
892 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
893 mode->c_cc[VMIN] = 1;
894 mode->c_cc[VTIME] = 0;
897 else if (IXANY && info->name == decctlq) {
899 mode->c_iflag |= IXANY;
901 mode->c_iflag &= ~IXANY;
903 else if (TABDLY && info->name == stty_tabs) {
905 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
907 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
909 else if (OXTABS && info->name == stty_tabs) {
911 mode->c_oflag |= OXTABS;
913 mode->c_oflag &= ~OXTABS;
915 else if (XCASE && IUCLC && OLCUC
916 && (info->name == stty_lcase || info->name == stty_LCASE)) {
918 mode->c_lflag &= ~XCASE;
919 mode->c_iflag &= ~IUCLC;
920 mode->c_oflag &= ~OLCUC;
922 mode->c_lflag |= XCASE;
923 mode->c_iflag |= IUCLC;
924 mode->c_oflag |= OLCUC;
927 else if (info->name == stty_crt) {
928 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
930 else if (info->name == stty_dec) {
931 mode->c_cc[VINTR] = 3; /* ^C */
932 mode->c_cc[VERASE] = 127; /* DEL */
933 mode->c_cc[VKILL] = 21; /* ^U */
934 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
935 if (IXANY) mode->c_iflag &= ~IXANY;
939 static void display_changed(const struct termios *mode)
944 int prev_type = control;
946 display_speed(mode, 1);
948 wrapf("line = %d;\n", mode->c_line);
953 for (i = 0; control_info[i].name != stty_min; ++i) {
954 if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
956 /* If swtch is the same as susp, don't print both */
958 if (control_info[i].name == stty_swtch)
961 /* If eof uses the same slot as min, only print whichever applies */
963 if ((mode->c_lflag & ICANON) == 0
964 && (control_info[i].name == stty_eof
965 || control_info[i].name == stty_eol)) continue;
967 wrapf("%s = %s;", control_info[i].name,
968 visible(mode->c_cc[control_info[i].offset]));
970 if ((mode->c_lflag & ICANON) == 0) {
971 wrapf("min = %d; time = %d;", (int) mode->c_cc[VMIN],
972 (int) mode->c_cc[VTIME]);
974 if (current_col) wrapf("\n");
976 for (i = 0; i < NUM_mode_info; ++i) {
977 if (mode_info[i].flags & OMIT)
979 if (mode_info[i].type != prev_type) {
980 if (current_col) wrapf("\n");
981 prev_type = mode_info[i].type;
984 bitsp = mode_type_flag(mode_info[i].type, mode);
985 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
986 if ((*bitsp & mask) == mode_info[i].bits) {
987 if (mode_info[i].flags & SANE_UNSET) {
988 wrapf("%s", mode_info[i].name);
990 } else if ((mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)) {
991 wrapf("-%s", mode_info[i].name);
994 if (current_col) wrapf("\n");
997 static void set_control_char_or_die(const struct control_info *info,
998 const char *arg, struct termios *mode)
1000 unsigned char value;
1002 if (info->name == stty_min || info->name == stty_time)
1003 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1004 else if (arg[0] == '\0' || arg[1] == '\0')
1006 else if (streq(arg, "^-") || streq(arg, "undef"))
1007 value = _POSIX_VDISABLE;
1008 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1009 value = arg[1] & 0x1f; /* Non-letters get weird results */
1013 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1014 mode->c_cc[info->offset] = value;
1017 int stty_main(int argc, char **argv)
1019 struct termios mode;
1020 void (*output_func)(const struct termios *);
1021 const char *file_name = NULL;
1022 int require_set_attr;
1025 int recoverable_output;
1029 output_func = display_changed;
1032 require_set_attr = 0;
1034 recoverable_output = 0;
1036 /* First pass: only parse/verify command line params */
1039 const struct mode_info *mp;
1040 const struct control_info *cp;
1041 const char *arg = argv[k];
1042 const char *argnext = argv[k+1];
1045 if (arg[0] == '-') {
1047 mp = find_mode(arg+1);
1049 if (!(mp->flags & REV))
1050 bb_error_msg_and_die("invalid argument '%s'", arg);
1054 /* It is an option - parse it */
1060 output_func = display_all;
1063 recoverable_output = 1;
1064 output_func = display_recoverable;
1068 bb_error_msg_and_die("only one device may be specified");
1069 file_name = &arg[i+1]; /* "-Fdevice" ? */
1070 if (!file_name[0]) { /* nope, "-F device" */
1071 int p = k+1; /* argv[p] is argnext */
1072 file_name = argnext;
1074 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1075 /* remove -F param from arg[vc] */
1077 while (argv[p]) { argv[p] = argv[p+1]; ++p; }
1081 bb_error_msg_and_die("invalid argument '%s'", arg);
1088 mp = find_mode(arg);
1094 cp = find_control(arg);
1097 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1098 /* called for the side effect of xfunc death only */
1099 set_control_char_or_die(cp, argnext, &mode);
1105 param = find_param(arg);
1106 if (param & param_need_arg) {
1108 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1116 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1118 # endif /* else fall-through */
1123 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1130 /* called for the side effect of xfunc death only */
1131 set_speed_or_die(input_speed, argnext, &mode);
1134 /* called for the side effect of xfunc death only */
1135 set_speed_or_die(output_speed, argnext, &mode);
1138 if (recover_mode(arg, &mode) == 1) break;
1139 if (string_to_baud_or_die(arg) != (speed_t) -1) break;
1140 bb_error_msg_and_die("invalid argument '%s'", arg);
1145 /* Specifying both -a and -g is an error */
1146 if (verbose_output && recoverable_output)
1147 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
1148 /* Specifying -a or -g with non-options is an error */
1149 if (!noargs && (verbose_output || recoverable_output))
1150 bb_error_msg_and_die("modes may not be set when specifying an output style");
1152 /* Now it is safe to start doing things */
1155 device_name = file_name;
1156 fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
1157 if (fd != STDIN_FILENO) {
1158 dup2(fd, STDIN_FILENO);
1161 fdflags = fcntl(STDIN_FILENO, F_GETFL);
1162 if (fdflags == -1 || fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
1163 perror_on_device_and_die("%s: cannot reset non-blocking mode");
1166 /* Initialize to all zeroes so there is no risk memcmp will report a
1167 spurious difference in an uninitialized portion of the structure */
1168 memset(&mode, 0, sizeof(mode));
1169 if (tcgetattr(STDIN_FILENO, &mode))
1170 perror_on_device_and_die("%s");
1172 if (verbose_output || recoverable_output || noargs) {
1173 max_col = screen_columns_or_die();
1175 return EXIT_SUCCESS;
1178 /* Second pass: perform actions */
1181 const struct mode_info *mp;
1182 const struct control_info *cp;
1183 const char *arg = argv[k];
1184 const char *argnext = argv[k+1];
1187 if (arg[0] == '-') {
1188 mp = find_mode(arg+1);
1190 set_mode(mp, 1 /* reversed */, &mode);
1191 require_set_attr = 1;
1193 /* It is an option - already parsed. Skip it */
1197 mp = find_mode(arg);
1199 set_mode(mp, 0 /* non-reversed */, &mode);
1200 require_set_attr = 1;
1204 cp = find_control(arg);
1207 set_control_char_or_die(cp, argnext, &mode);
1208 require_set_attr = 1;
1212 param = find_param(arg);
1213 if (param & param_need_arg) {
1220 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1221 require_set_attr = 1;
1226 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1229 display_window_size(0);
1232 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1236 display_speed(&mode, 0);
1239 set_speed_or_die(input_speed, argnext, &mode);
1241 require_set_attr = 1;
1244 set_speed_or_die(output_speed, argnext, &mode);
1246 require_set_attr = 1;
1249 if (recover_mode(arg, &mode) == 1)
1250 require_set_attr = 1;
1251 else /* true: if (string_to_baud_or_die(arg) != (speed_t) -1) */ {
1252 set_speed_or_die(both_speeds, arg, &mode);
1254 require_set_attr = 1;
1255 } /* else - impossible (caught in the first pass):
1256 bb_error_msg_and_die("invalid argument '%s'", arg); */
1260 if (require_set_attr) {
1261 struct termios new_mode;
1263 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1264 perror_on_device_and_die("%s");
1266 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1267 it performs *any* of the requested operations. This means it
1268 can report 'success' when it has actually failed to perform
1269 some proper subset of the requested operations. To detect
1270 this partial failure, get the current terminal attributes and
1271 compare them to the requested ones */
1273 /* Initialize to all zeroes so there is no risk memcmp will report a
1274 spurious difference in an uninitialized portion of the structure */
1275 memset(&new_mode, 0, sizeof(new_mode));
1276 if (tcgetattr(STDIN_FILENO, &new_mode))
1277 perror_on_device_and_die("%s");
1279 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1281 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1282 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1283 sometimes (m1 != m2). The only difference is in the four bits
1284 of the c_cflag field corresponding to the baud rate. To save
1285 Sun users a little confusion, don't report an error if this
1286 happens. But suppress the error only if we haven't tried to
1287 set the baud rate explicitly -- otherwise we'd never give an
1288 error for a true failure to set the baud rate */
1290 new_mode.c_cflag &= (~CIBAUD);
1291 if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1293 perror_on_device_and_die("%s: cannot perform all requested operations");
1297 return EXIT_SUCCESS;