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;
641 static int recover_mode(const char *arg, struct termios *mode);
642 static void set_mode(const struct mode_info *info,
643 int reversed, struct termios *mode);
644 static void display_all(const struct termios *mode);
645 static void display_changed(const struct termios *mode);
646 static void display_recoverable(const struct termios *mode);
647 static void display_speed(const struct termios *mode, int fancy);
648 static void sane_mode(struct termios *mode);
649 static void set_control_char_or_die(const struct control_info *info,
650 const char *arg, struct termios *mode);
652 int stty_main(int argc, char **argv)
655 void (*output_func)(const struct termios *);
656 const char *file_name = NULL;
657 int require_set_attr;
660 int recoverable_output;
664 output_func = display_changed;
667 require_set_attr = 0;
669 recoverable_output = 0;
671 /* First pass: only parse/verify command line params */
674 const struct mode_info *mp;
675 const struct control_info *cp;
676 const char *arg = argv[k];
677 const char *argnext = argv[k+1];
682 mp = find_mode(arg+1);
684 if (!(mp->flags & REV))
685 bb_error_msg_and_die("invalid argument '%s'", arg);
689 /* It is an option - parse it */
695 output_func = display_all;
698 recoverable_output = 1;
699 output_func = display_recoverable;
703 bb_error_msg_and_die("only one device may be specified");
704 file_name = &arg[i+1]; /* "-Fdevice" ? */
705 if (!file_name[0]) { /* nope, "-F device" */
706 int p = k+1; /* argv[p] is argnext */
709 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
710 /* remove -F param from arg[vc] */
712 while (argv[p]) { argv[p] = argv[p+1]; ++p; }
716 bb_error_msg_and_die("invalid argument '%s'", arg);
729 cp = find_control(arg);
732 bb_error_msg_and_die(bb_msg_requires_arg, arg);
733 /* called for the side effect of xfunc death only */
734 set_control_char_or_die(cp, argnext, &mode);
740 param = find_param(arg);
741 if (param & param_need_arg) {
743 bb_error_msg_and_die(bb_msg_requires_arg, arg);
751 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
753 # endif /* else fall-through */
758 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
765 /* called for the side effect of xfunc death only */
766 set_speed_or_die(input_speed, argnext, &mode);
769 /* called for the side effect of xfunc death only */
770 set_speed_or_die(output_speed, argnext, &mode);
773 if (recover_mode(arg, &mode) == 1) break;
774 if (string_to_baud_or_die(arg) != (speed_t) -1) break;
775 bb_error_msg_and_die("invalid argument '%s'", arg);
780 /* Specifying both -a and -g is an error */
781 if (verbose_output && recoverable_output)
782 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
783 /* Specifying -a or -g with non-options is an error */
784 if (!noargs && (verbose_output || recoverable_output))
785 bb_error_msg_and_die("modes may not be set when specifying an output style");
787 /* Now it is safe to start doing things */
790 device_name = file_name;
791 fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
792 if (fd != STDIN_FILENO) {
793 dup2(fd, STDIN_FILENO);
796 fdflags = fcntl(STDIN_FILENO, F_GETFL);
797 if (fdflags == -1 || fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
798 perror_on_device_and_die("%s: cannot reset non-blocking mode");
801 /* Initialize to all zeroes so there is no risk memcmp will report a
802 spurious difference in an uninitialized portion of the structure */
803 memset(&mode, 0, sizeof(mode));
804 if (tcgetattr(STDIN_FILENO, &mode))
805 perror_on_device_and_die("%s");
807 if (verbose_output || recoverable_output || noargs) {
808 max_col = screen_columns_or_die();
813 /* Second pass: perform actions */
816 const struct mode_info *mp;
817 const struct control_info *cp;
818 const char *arg = argv[k];
819 const char *argnext = argv[k+1];
823 mp = find_mode(arg+1);
825 set_mode(mp, 1 /* reversed */, &mode);
827 /* It is an option - already parsed. Skip it */
833 set_mode(mp, 0 /* non-reversed */, &mode);
837 cp = find_control(arg);
840 set_control_char_or_die(cp, argnext, &mode);
844 param = find_param(arg);
845 if (param & param_need_arg) {
852 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
853 require_set_attr = 1;
858 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
861 display_window_size(0);
864 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
868 display_speed(&mode, 0);
871 set_speed_or_die(input_speed, argnext, &mode);
873 require_set_attr = 1;
876 set_speed_or_die(output_speed, argnext, &mode);
878 require_set_attr = 1;
881 if (recover_mode(arg, &mode) == 1)
882 require_set_attr = 1;
883 else /* true: if (string_to_baud_or_die(arg) != (speed_t) -1) */ {
884 set_speed_or_die(both_speeds, arg, &mode);
886 require_set_attr = 1;
887 } /* else - impossible (caught in the first pass):
888 bb_error_msg_and_die("invalid argument '%s'", arg); */
892 if (require_set_attr) {
893 struct termios new_mode;
895 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
896 perror_on_device_and_die("%s");
898 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
899 it performs *any* of the requested operations. This means it
900 can report 'success' when it has actually failed to perform
901 some proper subset of the requested operations. To detect
902 this partial failure, get the current terminal attributes and
903 compare them to the requested ones */
905 /* Initialize to all zeroes so there is no risk memcmp will report a
906 spurious difference in an uninitialized portion of the structure */
907 memset(&new_mode, 0, sizeof(new_mode));
908 if (tcgetattr(STDIN_FILENO, &new_mode))
909 perror_on_device_and_die("%s");
911 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
913 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
914 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
915 sometimes (m1 != m2). The only difference is in the four bits
916 of the c_cflag field corresponding to the baud rate. To save
917 Sun users a little confusion, don't report an error if this
918 happens. But suppress the error only if we haven't tried to
919 set the baud rate explicitly -- otherwise we'd never give an
920 error for a true failure to set the baud rate */
922 new_mode.c_cflag &= (~CIBAUD);
923 if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
925 perror_on_device_and_die("%s: cannot perform all requested operations");
932 /* Save set_mode from #ifdef forest plague */
967 static void set_mode(const struct mode_info *info, int reversed,
968 struct termios *mode)
972 bitsp = mode_type_flag(info->type, mode);
976 *bitsp = *bitsp & ~info->mask & ~info->bits;
978 *bitsp = (*bitsp & ~info->mask) | info->bits;
982 /* Combination mode */
983 if (info->name == evenp || info->name == parity) {
985 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
987 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
988 } else if (info->name == stty_oddp) {
990 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
992 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
993 } else if (info->name == stty_nl) {
995 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
996 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
998 mode->c_iflag = mode->c_iflag & ~ICRNL;
999 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1001 } else if (info->name == stty_ek) {
1002 mode->c_cc[VERASE] = CERASE;
1003 mode->c_cc[VKILL] = CKILL;
1004 } else if (info->name == stty_sane) {
1007 else if (info->name == cbreak) {
1009 mode->c_lflag |= ICANON;
1011 mode->c_lflag &= ~ICANON;
1012 } else if (info->name == stty_pass8) {
1014 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1015 mode->c_iflag |= ISTRIP;
1017 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1018 mode->c_iflag &= ~ISTRIP;
1020 } else if (info->name == litout) {
1022 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1023 mode->c_iflag |= ISTRIP;
1024 mode->c_oflag |= OPOST;
1026 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1027 mode->c_iflag &= ~ISTRIP;
1028 mode->c_oflag &= ~OPOST;
1030 } else if (info->name == raw || info->name == cooked) {
1031 if ((info->name[0] == 'r' && reversed)
1032 || (info->name[0] == 'c' && !reversed)) {
1034 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1035 mode->c_oflag |= OPOST;
1036 mode->c_lflag |= ISIG | ICANON;
1038 mode->c_cc[VEOF] = CEOF;
1041 mode->c_cc[VEOL] = CEOL;
1046 mode->c_oflag &= ~OPOST;
1047 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1048 mode->c_cc[VMIN] = 1;
1049 mode->c_cc[VTIME] = 0;
1052 else if (IXANY && info->name == decctlq) {
1054 mode->c_iflag |= IXANY;
1056 mode->c_iflag &= ~IXANY;
1058 else if (TABDLY && info->name == stty_tabs) {
1060 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1062 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1064 else if (OXTABS && info->name == stty_tabs) {
1066 mode->c_oflag |= OXTABS;
1068 mode->c_oflag &= ~OXTABS;
1070 else if (XCASE && IUCLC && OLCUC
1071 && (info->name == stty_lcase || info->name == stty_LCASE)) {
1073 mode->c_lflag &= ~XCASE;
1074 mode->c_iflag &= ~IUCLC;
1075 mode->c_oflag &= ~OLCUC;
1077 mode->c_lflag |= XCASE;
1078 mode->c_iflag |= IUCLC;
1079 mode->c_oflag |= OLCUC;
1082 else if (info->name == stty_crt) {
1083 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1085 else if (info->name == stty_dec) {
1086 mode->c_cc[VINTR] = 3; /* ^C */
1087 mode->c_cc[VERASE] = 127; /* DEL */
1088 mode->c_cc[VKILL] = 21; /* ^U */
1089 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1090 if (IXANY) mode->c_iflag &= ~IXANY;
1094 static void set_control_char_or_die(const struct control_info *info,
1095 const char *arg, struct termios *mode)
1097 unsigned char value;
1099 if (info->name == stty_min || info->name == stty_time)
1100 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1101 else if (arg[0] == '\0' || arg[1] == '\0')
1103 else if (streq(arg, "^-") || streq(arg, "undef"))
1104 value = _POSIX_VDISABLE;
1105 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1106 value = arg[1] & 0x1f; /* Non-letters get weird results */
1110 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1111 mode->c_cc[info->offset] = value;
1114 static void display_changed(const struct termios *mode)
1119 int prev_type = control;
1121 display_speed(mode, 1);
1123 wrapf("line = %d;\n", mode->c_line);
1128 for (i = 0; control_info[i].name != stty_min; ++i) {
1129 if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
1131 /* If swtch is the same as susp, don't print both */
1133 if (control_info[i].name == stty_swtch)
1136 /* If eof uses the same slot as min, only print whichever applies */
1138 if ((mode->c_lflag & ICANON) == 0
1139 && (control_info[i].name == stty_eof
1140 || control_info[i].name == stty_eol)) continue;
1142 wrapf("%s = %s;", control_info[i].name,
1143 visible(mode->c_cc[control_info[i].offset]));
1145 if ((mode->c_lflag & ICANON) == 0) {
1146 wrapf("min = %d; time = %d;", (int) mode->c_cc[VMIN],
1147 (int) mode->c_cc[VTIME]);
1149 if (current_col) wrapf("\n");
1151 for (i = 0; i < NUM_mode_info; ++i) {
1152 if (mode_info[i].flags & OMIT)
1154 if (mode_info[i].type != prev_type) {
1155 if (current_col) wrapf("\n");
1156 prev_type = mode_info[i].type;
1159 bitsp = mode_type_flag(mode_info[i].type, mode);
1160 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1161 if ((*bitsp & mask) == mode_info[i].bits) {
1162 if (mode_info[i].flags & SANE_UNSET) {
1163 wrapf("%s", mode_info[i].name);
1165 } else if ((mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)) {
1166 wrapf("-%s", mode_info[i].name);
1169 if (current_col) wrapf("\n");
1172 static void display_all(const struct termios *mode)
1177 int prev_type = control;
1179 display_speed(mode, 1);
1180 display_window_size(1);
1182 wrapf("line = %d;\n", mode->c_line);
1187 for (i = 0; control_info[i].name != stty_min; ++i) {
1188 /* If swtch is the same as susp, don't print both */
1190 if (control_info[i].name == stty_swtch)
1193 /* If eof uses the same slot as min, only print whichever applies */
1195 if ((mode->c_lflag & ICANON) == 0
1196 && (control_info[i].name == stty_eof
1197 || control_info[i].name == stty_eol)) continue;
1199 wrapf("%s = %s;", control_info[i].name,
1200 visible(mode->c_cc[control_info[i].offset]));
1203 if ((mode->c_lflag & ICANON) == 0)
1205 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1206 if (current_col) wrapf("\n");
1208 for (i = 0; i < NUM_mode_info; ++i) {
1209 if (mode_info[i].flags & OMIT)
1211 if (mode_info[i].type != prev_type) {
1213 prev_type = mode_info[i].type;
1216 bitsp = mode_type_flag(mode_info[i].type, mode);
1217 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1218 if ((*bitsp & mask) == mode_info[i].bits)
1219 wrapf("%s", mode_info[i].name);
1220 else if (mode_info[i].flags & REV)
1221 wrapf("-%s", mode_info[i].name);
1223 if (current_col) wrapf("\n");
1226 static void display_speed(const struct termios *mode, int fancy)
1229 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
1230 unsigned long ispeed, ospeed;
1232 ospeed = ispeed = cfgetispeed(mode);
1233 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
1234 ispeed = ospeed; /* in case ispeed was 0 */
1236 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
1238 if (fancy) fmt_str += 9;
1239 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
1242 static void display_recoverable(const struct termios *mode)
1245 printf("%lx:%lx:%lx:%lx",
1246 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1247 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1248 for (i = 0; i < NCCS; ++i)
1249 printf(":%x", (unsigned int) mode->c_cc[i]);
1253 static int recover_mode(const char *arg, struct termios *mode)
1257 unsigned long iflag, oflag, cflag, lflag;
1259 /* Scan into temporaries since it is too much trouble to figure out
1260 the right format for 'tcflag_t' */
1261 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
1262 &iflag, &oflag, &cflag, &lflag, &n) != 4)
1264 mode->c_iflag = iflag;
1265 mode->c_oflag = oflag;
1266 mode->c_cflag = cflag;
1267 mode->c_lflag = lflag;
1269 for (i = 0; i < NCCS; ++i) {
1270 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
1272 mode->c_cc[i] = chr;
1276 /* Fail if there are too many fields */
1283 static void sane_mode(struct termios *mode)
1288 for (i = 0; i < NUM_control_info; ++i) {
1290 if (control_info[i].name == stty_min)
1293 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1296 for (i = 0; i < NUM_mode_info; ++i) {
1297 if (mode_info[i].flags & SANE_SET) {
1298 bitsp = mode_type_flag(mode_info[i].type, mode);
1299 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
1300 | mode_info[i].bits;
1301 } else if (mode_info[i].flags & SANE_UNSET) {
1302 bitsp = mode_type_flag(mode_info[i].type, mode);
1303 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
1304 & ~mode_info[i].bits;