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 * 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 = (sizeof(mode_info) / sizeof(mode_info[0]))
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 = (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 void set_speed_or_die(enum speed_setting type, const char * const arg,
429 struct termios * const mode)
433 baud = tty_value_to_baud(xatou(arg));
435 if (type != output_speed) { /* either input or both */
436 cfsetispeed(mode, baud);
438 if (type != input_speed) { /* either output or both */
439 cfsetospeed(mode, baud);
443 static ATTRIBUTE_NORETURN void perror_on_device_and_die(const char *fmt)
445 bb_perror_msg_and_die(fmt, device_name);
448 static void perror_on_device(const char *fmt)
450 bb_perror_msg(fmt, device_name);
453 /* Print format string MESSAGE and optional args.
454 Wrap to next line first if it won't fit.
455 Print a space first unless MESSAGE will start a new line */
456 static void wrapf(const char *message, ...)
462 va_start(args, message);
463 buflen = vsnprintf(buf, sizeof(buf), message, args);
465 /* buflen = strlen(buf); cheaper not to pull in strlen */
466 if (!buflen /*|| buflen >= sizeof(buf)*/) return;
468 if (current_col > 0) {
470 if (buf[0] != '\n') {
471 if (current_col + buflen >= max_col) {
479 current_col += buflen;
480 if (buf[buflen-1] == '\n')
484 static void set_window_size(const int rows, const int cols)
486 struct winsize win = { 0, 0, 0, 0};
488 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
489 if (errno != EINVAL) {
492 memset(&win, 0, sizeof(win));
500 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
502 perror_on_device("%s");
505 static void display_window_size(const int fancy)
507 const char *fmt_str = "%s\0%s: no size information for this device";
508 unsigned width, height;
510 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
511 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
512 perror_on_device(fmt_str);
515 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
520 static const struct suffix_mult stty_suffixes[] = {
527 static const struct mode_info *find_mode(const char *name)
530 for (i = 0; i < NUM_mode_info; ++i)
531 if (!strcmp(name, mode_info[i].name))
532 return &mode_info[i];
536 static const struct control_info *find_control(const char *name)
539 for (i = 0; i < NUM_control_info; ++i)
540 if (!strcmp(name, control_info[i].name))
541 return &control_info[i];
546 param_need_arg = 0x80,
547 param_line = 1 | 0x80,
548 param_rows = 2 | 0x80,
549 param_cols = 3 | 0x80,
552 param_ispeed = 6 | 0x80,
553 param_ospeed = 7 | 0x80,
556 static int find_param(const char * const name)
558 const char * const params[] = {
569 int i = index_in_str_array(params, name);
571 if (!(i == 4 || i == 5))
577 static int recover_mode(const char *arg, struct termios *mode)
581 unsigned long iflag, oflag, cflag, lflag;
583 /* Scan into temporaries since it is too much trouble to figure out
584 the right format for 'tcflag_t' */
585 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
586 &iflag, &oflag, &cflag, &lflag, &n) != 4)
588 mode->c_iflag = iflag;
589 mode->c_oflag = oflag;
590 mode->c_cflag = cflag;
591 mode->c_lflag = lflag;
593 for (i = 0; i < NCCS; ++i) {
594 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
600 /* Fail if there are too many fields */
607 static void display_recoverable(const struct termios *mode,
608 const int ATTRIBUTE_UNUSED dummy)
611 printf("%lx:%lx:%lx:%lx",
612 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
613 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
614 for (i = 0; i < NCCS; ++i)
615 printf(":%x", (unsigned int) mode->c_cc[i]);
619 static void display_speed(const struct termios *mode, int fancy)
622 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
623 unsigned long ispeed, ospeed;
625 ospeed = ispeed = cfgetispeed(mode);
626 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
627 ispeed = ospeed; /* in case ispeed was 0 */
629 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
631 if (fancy) fmt_str += 9;
632 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
635 static void do_display(const struct termios *mode, const int all)
640 int prev_type = control;
642 display_speed(mode, 1);
644 display_window_size(1);
646 wrapf("line = %d;\n", mode->c_line);
651 for (i = 0; control_info[i].name != stty_min; ++i) {
652 /* If swtch is the same as susp, don't print both */
654 if (control_info[i].name == stty_swtch)
657 /* If eof uses the same slot as min, only print whichever applies */
659 if ((mode->c_lflag & ICANON) == 0
660 && (control_info[i].name == stty_eof
661 || control_info[i].name == stty_eol)) continue;
663 wrapf("%s = %s;", control_info[i].name,
664 visible(mode->c_cc[control_info[i].offset]));
667 if ((mode->c_lflag & ICANON) == 0)
669 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
670 if (current_col) wrapf("\n");
672 for (i = 0; i < NUM_mode_info; ++i) {
673 if (mode_info[i].flags & OMIT)
675 if (mode_info[i].type != prev_type) {
677 if (current_col) wrapf("\n");
678 prev_type = mode_info[i].type;
681 bitsp = mode_type_flag(mode_info[i].type, mode);
682 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
683 if ((*bitsp & mask) == mode_info[i].bits) {
684 if (all || (mode_info[i].flags & SANE_UNSET))
685 wrapf("%s", mode_info[i].name);
687 if ((all && mode_info[i].flags & REV) ||
689 (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)))
690 wrapf("-%s", mode_info[i].name);
693 if (current_col) wrapf("\n");
696 static void sane_mode(struct termios *mode)
701 for (i = 0; i < NUM_control_info; ++i) {
703 if (control_info[i].name == stty_min)
706 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
709 for (i = 0; i < NUM_mode_info; ++i) {
710 if (mode_info[i].flags & SANE_SET) {
711 bitsp = mode_type_flag(mode_info[i].type, mode);
712 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
714 } else if (mode_info[i].flags & SANE_UNSET) {
715 bitsp = mode_type_flag(mode_info[i].type, mode);
716 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
717 & ~mode_info[i].bits;
722 /* Save set_mode from #ifdef forest plague */
757 static void set_mode(const struct mode_info *info, int reversed,
758 struct termios *mode)
762 bitsp = mode_type_flag(info->type, mode);
766 *bitsp = *bitsp & ~info->mask & ~info->bits;
768 *bitsp = (*bitsp & ~info->mask) | info->bits;
772 /* Combination mode */
773 if (info->name == evenp || info->name == parity) {
775 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
777 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
778 } else if (info->name == stty_oddp) {
780 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
782 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
783 } else if (info->name == stty_nl) {
785 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
786 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
788 mode->c_iflag = mode->c_iflag & ~ICRNL;
789 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
791 } else if (info->name == stty_ek) {
792 mode->c_cc[VERASE] = CERASE;
793 mode->c_cc[VKILL] = CKILL;
794 } else if (info->name == stty_sane) {
797 else if (info->name == cbreak) {
799 mode->c_lflag |= ICANON;
801 mode->c_lflag &= ~ICANON;
802 } else if (info->name == stty_pass8) {
804 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
805 mode->c_iflag |= ISTRIP;
807 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
808 mode->c_iflag &= ~ISTRIP;
810 } else if (info->name == litout) {
812 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
813 mode->c_iflag |= ISTRIP;
814 mode->c_oflag |= OPOST;
816 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
817 mode->c_iflag &= ~ISTRIP;
818 mode->c_oflag &= ~OPOST;
820 } else if (info->name == raw || info->name == cooked) {
821 if ((info->name[0] == 'r' && reversed)
822 || (info->name[0] == 'c' && !reversed)) {
824 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
825 mode->c_oflag |= OPOST;
826 mode->c_lflag |= ISIG | ICANON;
828 mode->c_cc[VEOF] = CEOF;
831 mode->c_cc[VEOL] = CEOL;
836 mode->c_oflag &= ~OPOST;
837 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
838 mode->c_cc[VMIN] = 1;
839 mode->c_cc[VTIME] = 0;
842 else if (IXANY && info->name == decctlq) {
844 mode->c_iflag |= IXANY;
846 mode->c_iflag &= ~IXANY;
848 else if (TABDLY && info->name == stty_tabs) {
850 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
852 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
854 else if (OXTABS && info->name == stty_tabs) {
856 mode->c_oflag |= OXTABS;
858 mode->c_oflag &= ~OXTABS;
860 else if (XCASE && IUCLC && OLCUC
861 && (info->name == stty_lcase || info->name == stty_LCASE)) {
863 mode->c_lflag &= ~XCASE;
864 mode->c_iflag &= ~IUCLC;
865 mode->c_oflag &= ~OLCUC;
867 mode->c_lflag |= XCASE;
868 mode->c_iflag |= IUCLC;
869 mode->c_oflag |= OLCUC;
872 else if (info->name == stty_crt) {
873 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
875 else if (info->name == stty_dec) {
876 mode->c_cc[VINTR] = 3; /* ^C */
877 mode->c_cc[VERASE] = 127; /* DEL */
878 mode->c_cc[VKILL] = 21; /* ^U */
879 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
880 if (IXANY) mode->c_iflag &= ~IXANY;
884 static void set_control_char_or_die(const struct control_info *info,
885 const char *arg, struct termios *mode)
889 if (info->name == stty_min || info->name == stty_time)
890 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
891 else if (arg[0] == '\0' || arg[1] == '\0')
893 else if (!strcmp(arg, "^-") || !strcmp(arg, "undef"))
894 value = _POSIX_VDISABLE;
895 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
896 value = arg[1] & 0x1f; /* Non-letters get weird results */
900 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
901 mode->c_cc[info->offset] = value;
904 #define STTY_require_set_attr (1<<0)
905 #define STTY_speed_was_set (1<<1)
906 #define STTY_verbose_output (1<<2)
907 #define STTY_recoverable_output (1<<3)
908 #define STTY_noargs (1<<4)
909 int stty_main(int argc, char **argv)
912 void (*output_func)(const struct termios *, const int);
913 const char *file_name = NULL;
918 stty_state = STTY_noargs;
919 output_func = do_display;
921 /* First pass: only parse/verify command line params */
924 const struct mode_info *mp;
925 const struct control_info *cp;
926 const char *arg = argv[k];
927 const char *argnext = argv[k+1];
932 mp = find_mode(arg+1);
934 if (!(mp->flags & REV))
935 goto invalid_argument;
936 stty_state &= ~STTY_noargs;
939 /* It is an option - parse it */
944 stty_state |= STTY_verbose_output;
945 output_func = do_display;
949 stty_state |= STTY_recoverable_output;
950 output_func = display_recoverable;
954 bb_error_msg_and_die("only one device may be specified");
955 file_name = &arg[i+1]; /* "-Fdevice" ? */
956 if (!file_name[0]) { /* nope, "-F device" */
957 int p = k+1; /* argv[p] is argnext */
960 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
961 /* remove -F param from arg[vc] */
963 while (argv[p]) { argv[p] = argv[p+1]; ++p; }
967 goto invalid_argument;
976 stty_state &= ~STTY_noargs;
980 cp = find_control(arg);
983 bb_error_msg_and_die(bb_msg_requires_arg, arg);
984 /* called for the side effect of xfunc death only */
985 set_control_char_or_die(cp, argnext, &mode);
986 stty_state &= ~STTY_noargs;
991 param = find_param(arg);
992 if (param & param_need_arg) {
994 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1002 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1004 # endif /* else fall-through */
1009 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1016 /* called for the side effect of xfunc death only */
1017 set_speed_or_die(input_speed, argnext, &mode);
1020 /* called for the side effect of xfunc death only */
1021 set_speed_or_die(output_speed, argnext, &mode);
1024 if (recover_mode(arg, &mode) == 1) break;
1025 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1027 bb_error_msg_and_die("invalid argument '%s'", arg);
1029 stty_state &= ~STTY_noargs;
1032 /* Specifying both -a and -g is an error */
1033 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1034 (STTY_verbose_output | STTY_recoverable_output))
1035 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
1036 /* Specifying -a or -g with non-options is an error */
1037 if (!(stty_state & STTY_noargs) &&
1038 (stty_state & (STTY_verbose_output | STTY_recoverable_output)))
1039 bb_error_msg_and_die("modes may not be set when specifying an output style");
1041 /* Now it is safe to start doing things */
1044 device_name = file_name;
1045 fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
1046 if (fd != STDIN_FILENO) {
1047 dup2(fd, STDIN_FILENO);
1050 fdflags = fcntl(STDIN_FILENO, F_GETFL);
1052 fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
1053 perror_on_device_and_die("%s: cannot reset non-blocking mode");
1056 /* Initialize to all zeroes so there is no risk memcmp will report a
1057 spurious difference in an uninitialized portion of the structure */
1058 memset(&mode, 0, sizeof(mode));
1059 if (tcgetattr(STDIN_FILENO, &mode))
1060 perror_on_device_and_die("%s");
1062 if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
1063 get_terminal_width_height(STDOUT_FILENO, &max_col, NULL);
1064 output_func(&mode, display_all);
1065 return EXIT_SUCCESS;
1068 /* Second pass: perform actions */
1071 const struct mode_info *mp;
1072 const struct control_info *cp;
1073 const char *arg = argv[k];
1074 const char *argnext = argv[k+1];
1077 if (arg[0] == '-') {
1078 mp = find_mode(arg+1);
1080 set_mode(mp, 1 /* reversed */, &mode);
1081 stty_state |= STTY_require_set_attr;
1083 /* It is an option - already parsed. Skip it */
1087 mp = find_mode(arg);
1089 set_mode(mp, 0 /* non-reversed */, &mode);
1090 stty_state |= STTY_require_set_attr;
1094 cp = find_control(arg);
1097 set_control_char_or_die(cp, argnext, &mode);
1098 stty_state |= STTY_require_set_attr;
1102 param = find_param(arg);
1103 if (param & param_need_arg) {
1110 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1111 stty_state |= STTY_require_set_attr;
1116 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1119 display_window_size(0);
1122 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1126 display_speed(&mode, 0);
1129 set_speed_or_die(input_speed, argnext, &mode);
1130 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1133 set_speed_or_die(output_speed, argnext, &mode);
1134 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1137 if (recover_mode(arg, &mode) == 1)
1138 stty_state |= STTY_require_set_attr;
1139 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
1140 set_speed_or_die(both_speeds, arg, &mode);
1141 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1142 } /* else - impossible (caught in the first pass):
1143 bb_error_msg_and_die("invalid argument '%s'", arg); */
1147 if (stty_state & STTY_require_set_attr) {
1148 struct termios new_mode;
1150 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1151 perror_on_device_and_die("%s");
1153 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1154 it performs *any* of the requested operations. This means it
1155 can report 'success' when it has actually failed to perform
1156 some proper subset of the requested operations. To detect
1157 this partial failure, get the current terminal attributes and
1158 compare them to the requested ones */
1160 /* Initialize to all zeroes so there is no risk memcmp will report a
1161 spurious difference in an uninitialized portion of the structure */
1162 memset(&new_mode, 0, sizeof(new_mode));
1163 if (tcgetattr(STDIN_FILENO, &new_mode))
1164 perror_on_device_and_die("%s");
1166 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1168 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1169 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1170 sometimes (m1 != m2). The only difference is in the four bits
1171 of the c_cflag field corresponding to the baud rate. To save
1172 Sun users a little confusion, don't report an error if this
1173 happens. But suppress the error only if we haven't tried to
1174 set the baud rate explicitly -- otherwise we'd never give an
1175 error for a true failure to set the baud rate */
1177 new_mode.c_cflag &= (~CIBAUD);
1178 if ((stty_state & STTY_speed_was_set)
1179 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1181 perror_on_device_and_die("%s: cannot perform all requested operations");
1185 return EXIT_SUCCESS;