1 /* vi: set sw=4 ts=4: */
3 * stty -- change and print terminal line settings
4 * Copyright (C) 1990-1999 Free Software Foundation, Inc.
6 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 /* Usage: stty [-ag] [-F device] [setting...]
11 * -a Write all current settings to stdout in human-readable form.
12 * -g Write all current settings to stdout in stty-readable form.
13 * -F Open and use the specified device instead of stdin
15 * If no args are given, write to stdout the baud rate and settings that
16 * have been changed from their defaults. Mode reading and changes
17 * are done on the specified device, or stdin if none was specified.
19 * David MacKenzie <djm@gnu.ai.mit.edu>
21 * Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
24 //config: bool "stty (8.6 kb)"
27 //config: stty is used to change and print terminal line settings.
29 //applet:IF_STTY(APPLET_NOEXEC(stty, stty, BB_DIR_BIN, BB_SUID_DROP, stty))
31 //kbuild:lib-$(CONFIG_STTY) += stty.o
33 //usage:#define stty_trivial_usage
34 //usage: "[-a|g] [-F DEVICE] [SETTING]..."
35 //usage:#define stty_full_usage "\n\n"
36 //usage: "Without arguments, prints baud rate, line discipline,\n"
37 //usage: "and deviations from stty sane\n"
38 //usage: "\n -F DEVICE Open device instead of stdin"
39 //usage: "\n -a Print all current settings in human-readable form"
40 //usage: "\n -g Print in stty-readable form"
41 //usage: "\n [SETTING] See manpage"
44 #include "common_bufsiz.h"
46 #ifndef _POSIX_VDISABLE
47 # define _POSIX_VDISABLE ((unsigned char) 0)
50 #define Control(c) ((c) & 0x1f)
51 /* Canonical values for control characters */
53 # define CINTR Control('c')
62 # define CKILL Control('u')
65 # define CEOF Control('d')
68 # define CEOL _POSIX_VDISABLE
71 # define CSTART Control('q')
74 # define CSTOP Control('s')
77 # define CSUSP Control('z')
79 #if defined(VEOL2) && !defined(CEOL2)
80 # define CEOL2 _POSIX_VDISABLE
82 /* glibc-2.12.1 uses only VSWTC name */
83 #if defined(VSWTC) && !defined(VSWTCH)
86 /* ISC renamed swtch to susp for termios, but we'll accept either name */
87 #if defined(VSUSP) && !defined(VSWTCH)
91 #if defined(VSWTCH) && !defined(CSWTCH)
92 # define CSWTCH _POSIX_VDISABLE
95 /* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'.
96 So the default is to disable 'swtch.' */
97 #if defined(__sparc__) && defined(__svr4__)
99 # define CSWTCH _POSIX_VDISABLE
102 #if defined(VWERSE) && !defined(VWERASE) /* AIX-3.2.5 */
103 # define VWERASE VWERSE
105 #if defined(VDSUSP) && !defined(CDSUSP)
106 # define CDSUSP Control('y')
108 #if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */
109 # define VREPRINT VRPRNT
111 #if defined(VREPRINT) && !defined(CRPRNT)
112 # define CRPRNT Control('r')
114 #if defined(VWERASE) && !defined(CWERASE)
115 # define CWERASE Control('w')
117 #if defined(VLNEXT) && !defined(CLNEXT)
118 # define CLNEXT Control('v')
120 #if defined(VDISCARD) && !defined(VFLUSHO)
121 # define VFLUSHO VDISCARD
123 #if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */
124 # define VFLUSHO VFLUSH
126 #if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */
127 # define ECHOCTL CTLECH
129 #if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */
130 # define ECHOCTL TCTLECH
132 #if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */
133 # define ECHOKE CRTKIL
135 #if defined(VFLUSHO) && !defined(CFLUSHO)
136 # define CFLUSHO Control('o')
138 #if defined(VSTATUS) && !defined(CSTATUS)
139 # define CSTATUS Control('t')
142 /* Save us from #ifdef forest plague */
255 /* Which speeds to set */
257 input_speed, output_speed, both_speeds
260 /* Which member(s) of 'struct termios' a mode uses */
262 control, input, output, local, combination
264 static tcflag_t *get_ptr_to_tcflag(unsigned type, const struct termios *mode)
266 static const uint8_t tcflag_offsets[] ALIGN1 = {
267 offsetof(struct termios, c_cflag), /* control */
268 offsetof(struct termios, c_iflag), /* input */
269 offsetof(struct termios, c_oflag), /* output */
270 offsetof(struct termios, c_lflag) /* local */
273 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
278 /* Flags for 'struct mode_info' */
279 #define SANE_SET 1 /* Set in 'sane' mode */
280 #define SANE_UNSET 2 /* Unset in 'sane' mode */
281 #define REV 4 /* Can be turned off by prepending '-' */
282 #define OMIT 8 /* Don't display value */
286 * This structure should be kept as small as humanly possible.
289 const uint8_t type; /* Which structure element to change */
290 const uint8_t flags; /* Setting and display options */
291 /* only these values are ever used, so... */
292 #if (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100
294 #elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000
297 const tcflag_t mask; /* Other bits to turn off for this mode */
299 /* was using short here, but ppc32 was unhappy */
300 const tcflag_t bits; /* Bits to set for this mode */
304 /* Must match mode_name[] and mode_info[] order! */
324 #if XCASE && IUCLC && OLCUC
330 #define MI_ENTRY(N,T,F,B,M) N "\0"
332 /* Mode names given on command line */
333 static const char mode_name[] ALIGN1 =
334 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
335 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
336 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
337 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
338 MI_ENTRY("ek", combination, OMIT, 0, 0 )
339 MI_ENTRY("sane", combination, OMIT, 0, 0 )
340 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
341 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
342 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
343 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
344 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
345 MI_ENTRY("crt", combination, OMIT, 0, 0 )
346 MI_ENTRY("dec", combination, OMIT, 0, 0 )
348 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
351 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
353 #if XCASE && IUCLC && OLCUC
354 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 )
355 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 )
357 MI_ENTRY("parenb", control, REV, PARENB, 0 )
358 MI_ENTRY("parodd", control, REV, PARODD, 0 )
360 MI_ENTRY("cmspar", control, REV, CMSPAR, 0 )
362 MI_ENTRY("cs5", control, 0, CS5, CSIZE)
363 MI_ENTRY("cs6", control, 0, CS6, CSIZE)
364 MI_ENTRY("cs7", control, 0, CS7, CSIZE)
365 MI_ENTRY("cs8", control, 0, CS8, CSIZE)
366 MI_ENTRY("hupcl", control, REV, HUPCL, 0 )
367 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 )
368 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 )
369 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 )
370 MI_ENTRY("clocal", control, REV, CLOCAL, 0 )
372 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
374 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 )
375 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 )
376 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 )
377 MI_ENTRY("parmrk", input, REV, PARMRK, 0 )
378 MI_ENTRY("inpck", input, REV, INPCK, 0 )
379 MI_ENTRY("istrip", input, REV, ISTRIP, 0 )
380 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 )
381 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 )
382 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
383 MI_ENTRY("ixon", input, REV, IXON, 0 )
384 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
385 MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 )
387 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
390 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
393 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
396 MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 )
398 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
400 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
403 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
406 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
409 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
412 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
415 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
418 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
421 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
422 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
425 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY)
426 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY)
427 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY)
428 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY)
432 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY)
434 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY)
437 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY)
439 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY)
442 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
447 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
448 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
451 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
452 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
455 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
456 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
458 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
459 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
461 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
463 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
464 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
465 MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 )
466 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
467 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
468 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
470 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
473 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
476 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
477 MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 )
480 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
481 MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 )
484 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
485 MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 )
487 MI_ENTRY("flusho", local, SANE_UNSET | REV, FLUSHO, 0 )
489 MI_ENTRY("extproc", local, SANE_UNSET | REV, EXTPROC, 0 )
494 #define MI_ENTRY(N,T,F,B,M) { T, F, M, B },
496 static const struct mode_info mode_info[] = {
497 /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */
498 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
499 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
500 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
501 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
502 MI_ENTRY("ek", combination, OMIT, 0, 0 )
503 MI_ENTRY("sane", combination, OMIT, 0, 0 )
504 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
505 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
506 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
507 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
508 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
509 MI_ENTRY("crt", combination, OMIT, 0, 0 )
510 MI_ENTRY("dec", combination, OMIT, 0, 0 )
512 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
515 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
517 #if XCASE && IUCLC && OLCUC
518 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 )
519 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 )
521 MI_ENTRY("parenb", control, REV, PARENB, 0 )
522 MI_ENTRY("parodd", control, REV, PARODD, 0 )
524 MI_ENTRY("cmspar", control, REV, CMSPAR, 0 )
526 MI_ENTRY("cs5", control, 0, CS5, CSIZE)
527 MI_ENTRY("cs6", control, 0, CS6, CSIZE)
528 MI_ENTRY("cs7", control, 0, CS7, CSIZE)
529 MI_ENTRY("cs8", control, 0, CS8, CSIZE)
530 MI_ENTRY("hupcl", control, REV, HUPCL, 0 )
531 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 )
532 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 )
533 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 )
534 MI_ENTRY("clocal", control, REV, CLOCAL, 0 )
536 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
538 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 )
539 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 )
540 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 )
541 MI_ENTRY("parmrk", input, REV, PARMRK, 0 )
542 MI_ENTRY("inpck", input, REV, INPCK, 0 )
543 MI_ENTRY("istrip", input, REV, ISTRIP, 0 )
544 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 )
545 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 )
546 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
547 MI_ENTRY("ixon", input, REV, IXON, 0 )
548 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
549 MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 )
551 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
554 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
557 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
560 MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 )
562 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
564 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
567 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
570 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
573 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
576 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
579 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
582 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
585 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
586 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
589 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY)
590 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY)
591 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY)
592 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY)
596 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY)
598 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY)
601 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY)
603 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY)
606 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
611 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
612 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
615 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
616 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
619 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
620 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
622 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
623 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
625 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
627 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
628 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
629 MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 )
630 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
631 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
632 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
634 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
637 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
640 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
641 MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 )
644 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
645 MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 )
648 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
649 MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 )
651 MI_ENTRY("flusho", local, SANE_UNSET | REV, FLUSHO, 0 )
653 MI_ENTRY("extproc", local, SANE_UNSET | REV, EXTPROC, 0 )
658 NUM_mode_info = ARRAY_SIZE(mode_info)
662 /* Control characters */
663 struct control_info {
664 const uint8_t saneval; /* Value to set for 'stty sane' */
665 const uint8_t offset; /* Offset in c_cc */
669 /* Must match control_name[] and control_info[] order! */
707 #define CI_ENTRY(n,s,o) n "\0"
709 /* Name given on command line */
710 static const char control_name[] ALIGN1 =
711 CI_ENTRY("intr", CINTR, VINTR )
712 CI_ENTRY("quit", CQUIT, VQUIT )
713 CI_ENTRY("erase", CERASE, VERASE )
714 CI_ENTRY("kill", CKILL, VKILL )
715 CI_ENTRY("eof", CEOF, VEOF )
716 CI_ENTRY("eol", CEOL, VEOL )
718 CI_ENTRY("eol2", CEOL2, VEOL2 )
721 CI_ENTRY("swtch", CSWTCH, VSWTCH )
723 CI_ENTRY("start", CSTART, VSTART )
724 CI_ENTRY("stop", CSTOP, VSTOP )
725 CI_ENTRY("susp", CSUSP, VSUSP )
727 CI_ENTRY("dsusp", CDSUSP, VDSUSP )
730 CI_ENTRY("rprnt", CRPRNT, VREPRINT)
733 CI_ENTRY("werase", CWERASE, VWERASE )
736 CI_ENTRY("lnext", CLNEXT, VLNEXT )
739 CI_ENTRY("flush", CFLUSHO, VFLUSHO )
742 CI_ENTRY("status", CSTATUS, VSTATUS )
744 /* These must be last because of the display routines */
745 CI_ENTRY("min", 1, VMIN )
746 CI_ENTRY("time", 0, VTIME )
750 #define CI_ENTRY(n,s,o) { s, o },
752 static const struct control_info control_info[] ALIGN2 = {
753 /* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */
754 CI_ENTRY("intr", CINTR, VINTR )
755 CI_ENTRY("quit", CQUIT, VQUIT )
756 CI_ENTRY("erase", CERASE, VERASE )
757 CI_ENTRY("kill", CKILL, VKILL )
758 CI_ENTRY("eof", CEOF, VEOF )
759 CI_ENTRY("eol", CEOL, VEOL )
761 CI_ENTRY("eol2", CEOL2, VEOL2 )
764 CI_ENTRY("swtch", CSWTCH, VSWTCH )
766 CI_ENTRY("start", CSTART, VSTART )
767 CI_ENTRY("stop", CSTOP, VSTOP )
768 CI_ENTRY("susp", CSUSP, VSUSP )
770 CI_ENTRY("dsusp", CDSUSP, VDSUSP )
773 CI_ENTRY("rprnt", CRPRNT, VREPRINT)
776 CI_ENTRY("werase", CWERASE, VWERASE )
779 CI_ENTRY("lnext", CLNEXT, VLNEXT )
782 CI_ENTRY("flush", CFLUSHO, VFLUSHO )
785 CI_ENTRY("status", CSTATUS, VSTATUS )
787 /* These must be last because of the display routines */
788 CI_ENTRY("min", 1, VMIN )
789 CI_ENTRY("time", 0, VTIME )
793 NUM_control_info = ARRAY_SIZE(control_info)
798 const char *device_name;
799 /* The width of the screen, for output wrapping */
801 /* Current position, to know when to wrap */
802 unsigned current_col;
804 #define G (*(struct globals*)bb_common_bufsiz1)
805 #define INIT_G() do { \
806 setup_common_bufsiz(); \
807 G.device_name = bb_msg_standard_input; \
809 G.current_col = 0; /* we are noexec, must clear */ \
812 static void set_speed_or_die(enum speed_setting type, const char *arg,
813 struct termios *mode)
817 baud = tty_value_to_baud(xatou(arg));
819 if (type != output_speed) { /* either input or both */
820 cfsetispeed(mode, baud);
822 if (type != input_speed) { /* either output or both */
823 cfsetospeed(mode, baud);
827 static NORETURN void perror_on_device_and_die(const char *fmt)
829 bb_perror_msg_and_die(fmt, G.device_name);
832 static void perror_on_device(const char *fmt)
834 bb_perror_msg(fmt, G.device_name);
837 /* Print format string MESSAGE and optional args.
838 Wrap to next line first if it won't fit.
839 Print a space first unless MESSAGE will start a new line */
840 static void wrapf(const char *message, ...)
846 va_start(args, message);
847 buflen = vsnprintf(buf, sizeof(buf), message, args);
849 /* We seem to be called only with suitable lengths, but check if
850 somebody failed to adhere to this assumption just to be sure. */
851 if (!buflen || buflen >= sizeof(buf)) return;
853 if (G.current_col > 0) {
855 if (buf[0] != '\n') {
856 if (G.current_col + buflen >= G.max_col) {
865 G.current_col += buflen;
866 if (buf[buflen-1] == '\n')
870 static void newline(void)
872 if (G.current_col != 0)
877 static void set_window_size(int rows, int cols)
879 struct winsize win = { 0, 0, 0, 0 };
881 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
882 if (errno != EINVAL) {
885 memset(&win, 0, sizeof(win));
893 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
895 perror_on_device("%s");
899 static void display_window_size(int fancy)
901 const char *fmt_str = "%s\0%s: no size information for this device";
902 unsigned width, height;
904 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
905 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
906 perror_on_device(fmt_str);
909 wrapf(fancy ? "rows %u; columns %u;" : "%u %u\n",
914 static const struct suffix_mult stty_suffixes[] = {
921 static const struct mode_info *find_mode(const char *name)
923 int i = index_in_strings(mode_name, name);
924 return i >= 0 ? &mode_info[i] : NULL;
927 static const struct control_info *find_control(const char *name)
929 int i = index_in_strings(control_name, name);
930 return i >= 0 ? &control_info[i] : NULL;
934 param_need_arg = 0x80,
935 param_line = 1 | 0x80,
936 param_rows = 2 | 0x80,
937 param_cols = 3 | 0x80,
938 param_columns = 4 | 0x80,
941 param_ispeed = 7 | 0x80,
942 param_ospeed = 8 | 0x80,
945 static int find_param(const char *name)
947 static const char params[] ALIGN1 =
956 int i = index_in_strings(params, name) + 1;
959 if (i != 5 && i != 6)
964 static int recover_mode(const char *arg, struct termios *mode)
968 unsigned long iflag, oflag, cflag, lflag;
970 /* Scan into temporaries since it is too much trouble to figure out
971 the right format for 'tcflag_t' */
972 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
973 &iflag, &oflag, &cflag, &lflag, &n) != 4)
975 mode->c_iflag = iflag;
976 mode->c_oflag = oflag;
977 mode->c_cflag = cflag;
978 mode->c_lflag = lflag;
980 for (i = 0; i < NCCS; ++i) {
981 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
987 /* Fail if there are too many fields */
994 static void display_recoverable(const struct termios *mode,
995 int UNUSED_PARAM dummy)
998 printf("%lx:%lx:%lx:%lx",
999 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1000 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1001 for (i = 0; i < NCCS; ++i)
1002 printf(":%x", (unsigned int) mode->c_cc[i]);
1006 static void display_speed(const struct termios *mode, int fancy)
1008 //____________________ 01234567 8 9
1009 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
1010 unsigned long ispeed, ospeed;
1012 ispeed = cfgetispeed(mode);
1013 ospeed = cfgetospeed(mode);
1014 if (ispeed == 0 || ispeed == ospeed) {
1015 ispeed = ospeed; /* in case ispeed was 0 */
1016 //________ 0123 4 5 6 7 8 9
1017 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
1019 if (fancy) fmt_str += 9;
1020 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
1023 static void do_display(const struct termios *mode, int all)
1028 int prev_type = control;
1030 display_speed(mode, 1);
1032 display_window_size(1);
1034 wrapf("line = %u;\n", mode->c_line);
1039 for (i = 0; i != CIDX_min; ++i) {
1043 /* If swtch is the same as susp, don't print both */
1045 if (i == CIDX_swtch)
1048 /* If eof uses the same slot as min, only print whichever applies */
1050 if (!(mode->c_lflag & ICANON)
1051 && (i == CIDX_eof || i == CIDX_eol)
1056 ch = mode->c_cc[control_info[i].offset];
1057 if (ch == _POSIX_VDISABLE)
1058 strcpy(buf10, "<undef>");
1060 visible(ch, buf10, 0);
1061 wrapf("%s = %s;", nth_string(control_name, i), buf10);
1064 if ((mode->c_lflag & ICANON) == 0)
1066 wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1069 for (i = 0; i < NUM_mode_info; ++i) {
1070 if (mode_info[i].flags & OMIT)
1072 if (mode_info[i].type != prev_type) {
1074 prev_type = mode_info[i].type;
1077 bitsp = get_ptr_to_tcflag(mode_info[i].type, mode);
1078 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1079 if ((*bitsp & mask) == mode_info[i].bits) {
1080 if (all || (mode_info[i].flags & SANE_UNSET))
1081 wrapf("-%s"+1, nth_string(mode_name, i));
1083 if ((all && mode_info[i].flags & REV)
1084 || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
1086 wrapf("-%s", nth_string(mode_name, i));
1093 static void sane_mode(struct termios *mode)
1097 for (i = 0; i < NUM_control_info; ++i) {
1102 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1105 for (i = 0; i < NUM_mode_info; ++i) {
1107 tcflag_t *bitsp = get_ptr_to_tcflag(mode_info[i].type, mode);
1111 val = *bitsp & ~((unsigned long)mode_info[i].mask);
1112 if (mode_info[i].flags & SANE_SET) {
1113 *bitsp = val | mode_info[i].bits;
1115 if (mode_info[i].flags & SANE_UNSET) {
1116 *bitsp = val & ~mode_info[i].bits;
1121 static void set_mode(const struct mode_info *info, int reversed,
1122 struct termios *mode)
1126 bitsp = get_ptr_to_tcflag(info->type, mode);
1129 tcflag_t val = *bitsp & ~info->mask;
1131 *bitsp = val & ~info->bits;
1133 *bitsp = val | info->bits;
1137 /* !bitsp - it's a "combination" mode */
1138 if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
1140 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1142 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
1143 } else if (info == &mode_info[IDX_oddp]) {
1145 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1147 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
1148 } else if (info == &mode_info[IDX_nl]) {
1150 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
1151 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
1153 mode->c_iflag = mode->c_iflag & ~ICRNL;
1154 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1156 } else if (info == &mode_info[IDX_ek]) {
1157 mode->c_cc[VERASE] = CERASE;
1158 mode->c_cc[VKILL] = CKILL;
1159 } else if (info == &mode_info[IDX_sane]) {
1161 } else if (info == &mode_info[IDX_cbreak]) {
1163 mode->c_lflag |= ICANON;
1165 mode->c_lflag &= ~ICANON;
1166 } else if (info == &mode_info[IDX_pass8]) {
1168 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1169 mode->c_iflag |= ISTRIP;
1171 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1172 mode->c_iflag &= ~ISTRIP;
1174 } else if (info == &mode_info[IDX_litout]) {
1176 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1177 mode->c_iflag |= ISTRIP;
1178 mode->c_oflag |= OPOST;
1180 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1181 mode->c_iflag &= ~ISTRIP;
1182 mode->c_oflag &= ~OPOST;
1184 } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
1185 if ((info == &mode_info[IDX_raw] && reversed)
1186 || (info == &mode_info[IDX_cooked] && !reversed)
1189 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1190 mode->c_oflag |= OPOST;
1191 mode->c_lflag |= ISIG | ICANON;
1193 mode->c_cc[VEOF] = CEOF;
1196 mode->c_cc[VEOL] = CEOL;
1201 mode->c_oflag &= ~OPOST;
1202 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1203 mode->c_cc[VMIN] = 1;
1204 mode->c_cc[VTIME] = 0;
1208 else if (info == &mode_info[IDX_decctlq]) {
1210 mode->c_iflag |= IXANY;
1212 mode->c_iflag &= ~IXANY;
1216 else if (info == &mode_info[IDX_tabs]) {
1218 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1220 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1224 else if (info == &mode_info[IDX_tabs]) {
1226 mode->c_oflag |= OXTABS;
1228 mode->c_oflag &= ~OXTABS;
1231 #if XCASE && IUCLC && OLCUC
1232 else if (info==&mode_info[IDX_lcase] || info==&mode_info[IDX_LCASE]) {
1234 mode->c_lflag &= ~XCASE;
1235 mode->c_iflag &= ~IUCLC;
1236 mode->c_oflag &= ~OLCUC;
1238 mode->c_lflag |= XCASE;
1239 mode->c_iflag |= IUCLC;
1240 mode->c_oflag |= OLCUC;
1244 else if (info == &mode_info[IDX_crt]) {
1245 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1246 } else if (info == &mode_info[IDX_dec]) {
1247 mode->c_cc[VINTR] = 3; /* ^C */
1248 mode->c_cc[VERASE] = 127; /* DEL */
1249 mode->c_cc[VKILL] = 21; /* ^U */
1250 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1251 if (IXANY) mode->c_iflag &= ~IXANY;
1255 static void set_control_char_or_die(const struct control_info *info,
1256 const char *arg, struct termios *mode)
1258 unsigned char value;
1260 if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
1261 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1262 else if (arg[0] == '\0' || arg[1] == '\0')
1264 else if (strcmp(arg, "^-") == 0 || strcmp(arg, "undef") == 0)
1265 value = _POSIX_VDISABLE;
1266 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1267 value = arg[1] & 0x1f; /* Non-letters get weird results */
1271 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1272 mode->c_cc[info->offset] = value;
1275 #define STTY_require_set_attr (1 << 0)
1276 #define STTY_speed_was_set (1 << 1)
1277 #define STTY_verbose_output (1 << 2)
1278 #define STTY_recoverable_output (1 << 3)
1279 #define STTY_noargs (1 << 4)
1281 int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1282 int stty_main(int argc UNUSED_PARAM, char **argv)
1284 struct termios mode;
1285 void (*output_func)(const struct termios *, int);
1286 const char *file_name = NULL;
1287 int display_all = 0;
1293 stty_state = STTY_noargs;
1294 output_func = do_display;
1296 /* First pass: only parse/verify command line params */
1299 const struct mode_info *mp;
1300 const struct control_info *cp;
1301 const char *arg = argv[k];
1302 const char *argnext = argv[k+1];
1305 if (arg[0] == '-') {
1307 mp = find_mode(arg+1);
1309 if (!(mp->flags & REV))
1310 goto invalid_argument;
1311 stty_state &= ~STTY_noargs;
1314 /* It is an option - parse it */
1319 stty_state |= STTY_verbose_output;
1320 output_func = do_display;
1324 stty_state |= STTY_recoverable_output;
1325 output_func = display_recoverable;
1329 bb_error_msg_and_die("only one device may be specified");
1330 file_name = &arg[i+1]; /* "-Fdevice" ? */
1331 if (!file_name[0]) { /* nope, "-F device" */
1332 int p = k+1; /* argv[p] is argnext */
1333 file_name = argnext;
1335 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1336 /* remove -F param from arg[vc] */
1338 argv[p] = argv[p+1];
1344 goto invalid_argument;
1351 mp = find_mode(arg);
1353 stty_state &= ~STTY_noargs;
1357 cp = find_control(arg);
1360 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1361 /* called for the side effect of xfunc death only */
1362 set_control_char_or_die(cp, argnext, &mode);
1363 stty_state &= ~STTY_noargs;
1368 param = find_param(arg);
1369 if (param & param_need_arg) {
1371 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1379 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1381 # endif /* else fall-through */
1387 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1394 /* called for the side effect of xfunc death only */
1395 set_speed_or_die(input_speed, argnext, &mode);
1398 /* called for the side effect of xfunc death only */
1399 set_speed_or_die(output_speed, argnext, &mode);
1402 if (recover_mode(arg, &mode) == 1) break;
1403 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1405 bb_error_msg_and_die("invalid argument '%s'", arg);
1407 stty_state &= ~STTY_noargs;
1410 /* Specifying both -a and -g is an error */
1411 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1412 (STTY_verbose_output | STTY_recoverable_output)
1414 bb_error_msg_and_die("-a and -g are mutually exclusive");
1416 /* Specifying -a or -g with non-options is an error */
1417 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output))
1418 && !(stty_state & STTY_noargs)
1420 bb_error_msg_and_die("modes may not be set when -a or -g is used");
1423 /* Now it is safe to start doing things */
1425 G.device_name = file_name;
1426 xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO);
1427 ndelay_off(STDIN_FILENO);
1430 /* Initialize to all zeroes so there is no risk memcmp will report a
1431 spurious difference in an uninitialized portion of the structure */
1432 memset(&mode, 0, sizeof(mode));
1433 if (tcgetattr(STDIN_FILENO, &mode))
1434 perror_on_device_and_die("%s");
1436 if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
1437 G.max_col = get_terminal_width(STDOUT_FILENO);
1438 output_func(&mode, display_all);
1439 return EXIT_SUCCESS;
1442 /* Second pass: perform actions */
1445 const struct mode_info *mp;
1446 const struct control_info *cp;
1447 const char *arg = argv[k];
1448 const char *argnext = argv[k+1];
1451 if (arg[0] == '-') {
1452 mp = find_mode(arg+1);
1454 set_mode(mp, 1 /* reversed */, &mode);
1455 stty_state |= STTY_require_set_attr;
1457 /* It is an option - already parsed. Skip it */
1461 mp = find_mode(arg);
1463 set_mode(mp, 0 /* non-reversed */, &mode);
1464 stty_state |= STTY_require_set_attr;
1468 cp = find_control(arg);
1471 set_control_char_or_die(cp, argnext, &mode);
1472 stty_state |= STTY_require_set_attr;
1476 param = find_param(arg);
1477 if (param & param_need_arg) {
1484 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1485 stty_state |= STTY_require_set_attr;
1491 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1494 display_window_size(0);
1497 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1501 display_speed(&mode, 0);
1504 set_speed_or_die(input_speed, argnext, &mode);
1505 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1508 set_speed_or_die(output_speed, argnext, &mode);
1509 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1512 if (recover_mode(arg, &mode) == 1)
1513 stty_state |= STTY_require_set_attr;
1514 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
1515 set_speed_or_die(both_speeds, arg, &mode);
1516 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1517 } /* else - impossible (caught in the first pass):
1518 bb_error_msg_and_die("invalid argument '%s'", arg); */
1522 if (stty_state & STTY_require_set_attr) {
1523 struct termios new_mode;
1525 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1526 perror_on_device_and_die("%s");
1528 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1529 it performs *any* of the requested operations. This means it
1530 can report 'success' when it has actually failed to perform
1531 some proper subset of the requested operations. To detect
1532 this partial failure, get the current terminal attributes and
1533 compare them to the requested ones */
1535 /* Initialize to all zeroes so there is no risk memcmp will report a
1536 spurious difference in an uninitialized portion of the structure */
1537 memset(&new_mode, 0, sizeof(new_mode));
1538 if (tcgetattr(STDIN_FILENO, &new_mode))
1539 perror_on_device_and_die("%s");
1541 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1543 * I think the below chunk is not necessary on Linux.
1544 * If you are deleting it, also delete STTY_speed_was_set bit -
1545 * it is only ever checked here.
1547 #if 0 /* was "if CIBAUD" */
1548 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1549 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1550 sometimes (m1 != m2). The only difference is in the four bits
1551 of the c_cflag field corresponding to the baud rate. To save
1552 Sun users a little confusion, don't report an error if this
1553 happens. But suppress the error only if we haven't tried to
1554 set the baud rate explicitly -- otherwise we'd never give an
1555 error for a true failure to set the baud rate */
1557 new_mode.c_cflag &= (~CIBAUD);
1558 if ((stty_state & STTY_speed_was_set)
1559 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1561 perror_on_device_and_die("%s: cannot perform all requested operations");
1565 return EXIT_SUCCESS;