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 GPLv2 or later, see file LICENSE in this source tree.
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
23 //config: bool "stty (8.6 kb)"
26 //config: stty is used to change and print terminal line settings.
28 //applet:IF_STTY(APPLET(stty, BB_DIR_BIN, BB_SUID_DROP))
30 //kbuild:lib-$(CONFIG_STTY) += stty.o
32 //usage:#define stty_trivial_usage
33 //usage: "[-a|g] [-F DEVICE] [SETTING]..."
34 //usage:#define stty_full_usage "\n\n"
35 //usage: "Without arguments, prints baud rate, line discipline,\n"
36 //usage: "and deviations from stty sane\n"
37 //usage: "\n -F DEVICE Open device instead of stdin"
38 //usage: "\n -a Print all current settings in human-readable form"
39 //usage: "\n -g Print in stty-readable form"
40 //usage: "\n [SETTING] See manpage"
43 #include "common_bufsiz.h"
45 #ifndef _POSIX_VDISABLE
46 # define _POSIX_VDISABLE ((unsigned char) 0)
49 #define Control(c) ((c) & 0x1f)
50 /* Canonical values for control characters */
52 # define CINTR Control('c')
61 # define CKILL Control('u')
64 # define CEOF Control('d')
67 # define CEOL _POSIX_VDISABLE
70 # define CSTART Control('q')
73 # define CSTOP Control('s')
76 # define CSUSP Control('z')
78 #if defined(VEOL2) && !defined(CEOL2)
79 # define CEOL2 _POSIX_VDISABLE
81 /* glibc-2.12.1 uses only VSWTC name */
82 #if defined(VSWTC) && !defined(VSWTCH)
85 /* ISC renamed swtch to susp for termios, but we'll accept either name */
86 #if defined(VSUSP) && !defined(VSWTCH)
90 #if defined(VSWTCH) && !defined(CSWTCH)
91 # define CSWTCH _POSIX_VDISABLE
94 /* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'.
95 So the default is to disable 'swtch.' */
96 #if defined(__sparc__) && defined(__svr4__)
98 # define CSWTCH _POSIX_VDISABLE
101 #if defined(VWERSE) && !defined(VWERASE) /* AIX-3.2.5 */
102 # define VWERASE VWERSE
104 #if defined(VDSUSP) && !defined(CDSUSP)
105 # define CDSUSP Control('y')
107 #if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */
108 # define VREPRINT VRPRNT
110 #if defined(VREPRINT) && !defined(CRPRNT)
111 # define CRPRNT Control('r')
113 #if defined(VWERASE) && !defined(CWERASE)
114 # define CWERASE Control('w')
116 #if defined(VLNEXT) && !defined(CLNEXT)
117 # define CLNEXT Control('v')
119 #if defined(VDISCARD) && !defined(VFLUSHO)
120 # define VFLUSHO VDISCARD
122 #if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */
123 # define VFLUSHO VFLUSH
125 #if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */
126 # define ECHOCTL CTLECH
128 #if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */
129 # define ECHOCTL TCTLECH
131 #if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */
132 # define ECHOKE CRTKIL
134 #if defined(VFLUSHO) && !defined(CFLUSHO)
135 # define CFLUSHO Control('o')
137 #if defined(VSTATUS) && !defined(CSTATUS)
138 # define CSTATUS Control('t')
141 /* Save us from #ifdef forest plague */
251 /* Which speeds to set */
253 input_speed, output_speed, both_speeds
256 /* Which member(s) of 'struct termios' a mode uses */
258 control, input, output, local, combination
260 static tcflag_t *get_ptr_to_tcflag(unsigned type, const struct termios *mode)
262 static const uint8_t tcflag_offsets[] ALIGN1 = {
263 offsetof(struct termios, c_cflag), /* control */
264 offsetof(struct termios, c_iflag), /* input */
265 offsetof(struct termios, c_oflag), /* output */
266 offsetof(struct termios, c_lflag) /* local */
269 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
274 /* Flags for 'struct mode_info' */
275 #define SANE_SET 1 /* Set in 'sane' mode */
276 #define SANE_UNSET 2 /* Unset in 'sane' mode */
277 #define REV 4 /* Can be turned off by prepending '-' */
278 #define OMIT 8 /* Don't display value */
282 * This structure should be kept as small as humanly possible.
285 const uint8_t type; /* Which structure element to change */
286 const uint8_t flags; /* Setting and display options */
287 /* only these values are ever used, so... */
288 #if (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100
290 #elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000
293 const tcflag_t mask; /* Other bits to turn off for this mode */
295 /* was using short here, but ppc32 was unhappy */
296 const tcflag_t bits; /* Bits to set for this mode */
300 /* Must match mode_name[] and mode_info[] order! */
320 #if XCASE && IUCLC && OLCUC
326 #define MI_ENTRY(N,T,F,B,M) N "\0"
328 /* Mode names given on command line */
329 static const char mode_name[] ALIGN1 =
330 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
331 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
332 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
333 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
334 MI_ENTRY("ek", combination, OMIT, 0, 0 )
335 MI_ENTRY("sane", combination, OMIT, 0, 0 )
336 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
337 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
338 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
339 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
340 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
341 MI_ENTRY("crt", combination, OMIT, 0, 0 )
342 MI_ENTRY("dec", combination, OMIT, 0, 0 )
344 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
347 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
349 #if XCASE && IUCLC && OLCUC
350 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 )
351 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 )
353 MI_ENTRY("parenb", control, REV, PARENB, 0 )
354 MI_ENTRY("parodd", control, REV, PARODD, 0 )
355 MI_ENTRY("cs5", control, 0, CS5, CSIZE)
356 MI_ENTRY("cs6", control, 0, CS6, CSIZE)
357 MI_ENTRY("cs7", control, 0, CS7, CSIZE)
358 MI_ENTRY("cs8", control, 0, CS8, CSIZE)
359 MI_ENTRY("hupcl", control, REV, HUPCL, 0 )
360 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 )
361 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 )
362 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 )
363 MI_ENTRY("clocal", control, REV, CLOCAL, 0 )
365 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
367 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 )
368 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 )
369 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 )
370 MI_ENTRY("parmrk", input, REV, PARMRK, 0 )
371 MI_ENTRY("inpck", input, REV, INPCK, 0 )
372 MI_ENTRY("istrip", input, REV, ISTRIP, 0 )
373 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 )
374 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 )
375 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
376 MI_ENTRY("ixon", input, REV, IXON, 0 )
377 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
378 MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 )
380 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
383 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
386 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
389 MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 )
391 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
393 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
396 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
399 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
402 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
405 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
408 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
411 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
414 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
415 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
418 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY)
419 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY)
420 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY)
421 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY)
425 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY)
427 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY)
430 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY)
432 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY)
435 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
440 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
441 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
444 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
445 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
448 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
449 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
451 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
452 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
454 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
456 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
457 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
458 MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 )
459 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
460 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
461 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
463 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
466 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
469 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
470 MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 )
473 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
474 MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 )
477 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
478 MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 )
483 #define MI_ENTRY(N,T,F,B,M) { T, F, M, B },
485 static const struct mode_info mode_info[] = {
486 /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */
487 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
488 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
489 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
490 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
491 MI_ENTRY("ek", combination, OMIT, 0, 0 )
492 MI_ENTRY("sane", combination, OMIT, 0, 0 )
493 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
494 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
495 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
496 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
497 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
498 MI_ENTRY("crt", combination, OMIT, 0, 0 )
499 MI_ENTRY("dec", combination, OMIT, 0, 0 )
501 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
504 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
506 #if XCASE && IUCLC && OLCUC
507 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 )
508 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 )
510 MI_ENTRY("parenb", control, REV, PARENB, 0 )
511 MI_ENTRY("parodd", control, REV, PARODD, 0 )
512 MI_ENTRY("cs5", control, 0, CS5, CSIZE)
513 MI_ENTRY("cs6", control, 0, CS6, CSIZE)
514 MI_ENTRY("cs7", control, 0, CS7, CSIZE)
515 MI_ENTRY("cs8", control, 0, CS8, CSIZE)
516 MI_ENTRY("hupcl", control, REV, HUPCL, 0 )
517 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 )
518 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 )
519 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 )
520 MI_ENTRY("clocal", control, REV, CLOCAL, 0 )
522 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
524 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 )
525 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 )
526 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 )
527 MI_ENTRY("parmrk", input, REV, PARMRK, 0 )
528 MI_ENTRY("inpck", input, REV, INPCK, 0 )
529 MI_ENTRY("istrip", input, REV, ISTRIP, 0 )
530 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 )
531 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 )
532 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
533 MI_ENTRY("ixon", input, REV, IXON, 0 )
534 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
535 MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 )
537 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
540 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
543 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
546 MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 )
548 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
550 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
553 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
556 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
559 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
562 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
565 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
568 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
571 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
572 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
575 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY)
576 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY)
577 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY)
578 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY)
582 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY)
584 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY)
587 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY)
589 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY)
592 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
597 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
598 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
601 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
602 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
605 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
606 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
608 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
609 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
611 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
613 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
614 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
615 MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 )
616 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
617 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
618 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
620 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
623 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
626 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
627 MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 )
630 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
631 MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 )
634 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
635 MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 )
640 NUM_mode_info = ARRAY_SIZE(mode_info)
644 /* Control characters */
645 struct control_info {
646 const uint8_t saneval; /* Value to set for 'stty sane' */
647 const uint8_t offset; /* Offset in c_cc */
651 /* Must match control_name[] and control_info[] order! */
689 #define CI_ENTRY(n,s,o) n "\0"
691 /* Name given on command line */
692 static const char control_name[] ALIGN1 =
693 CI_ENTRY("intr", CINTR, VINTR )
694 CI_ENTRY("quit", CQUIT, VQUIT )
695 CI_ENTRY("erase", CERASE, VERASE )
696 CI_ENTRY("kill", CKILL, VKILL )
697 CI_ENTRY("eof", CEOF, VEOF )
698 CI_ENTRY("eol", CEOL, VEOL )
700 CI_ENTRY("eol2", CEOL2, VEOL2 )
703 CI_ENTRY("swtch", CSWTCH, VSWTCH )
705 CI_ENTRY("start", CSTART, VSTART )
706 CI_ENTRY("stop", CSTOP, VSTOP )
707 CI_ENTRY("susp", CSUSP, VSUSP )
709 CI_ENTRY("dsusp", CDSUSP, VDSUSP )
712 CI_ENTRY("rprnt", CRPRNT, VREPRINT)
715 CI_ENTRY("werase", CWERASE, VWERASE )
718 CI_ENTRY("lnext", CLNEXT, VLNEXT )
721 CI_ENTRY("flush", CFLUSHO, VFLUSHO )
724 CI_ENTRY("status", CSTATUS, VSTATUS )
726 /* These must be last because of the display routines */
727 CI_ENTRY("min", 1, VMIN )
728 CI_ENTRY("time", 0, VTIME )
732 #define CI_ENTRY(n,s,o) { s, o },
734 static const struct control_info control_info[] ALIGN2 = {
735 /* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */
736 CI_ENTRY("intr", CINTR, VINTR )
737 CI_ENTRY("quit", CQUIT, VQUIT )
738 CI_ENTRY("erase", CERASE, VERASE )
739 CI_ENTRY("kill", CKILL, VKILL )
740 CI_ENTRY("eof", CEOF, VEOF )
741 CI_ENTRY("eol", CEOL, VEOL )
743 CI_ENTRY("eol2", CEOL2, VEOL2 )
746 CI_ENTRY("swtch", CSWTCH, VSWTCH )
748 CI_ENTRY("start", CSTART, VSTART )
749 CI_ENTRY("stop", CSTOP, VSTOP )
750 CI_ENTRY("susp", CSUSP, VSUSP )
752 CI_ENTRY("dsusp", CDSUSP, VDSUSP )
755 CI_ENTRY("rprnt", CRPRNT, VREPRINT)
758 CI_ENTRY("werase", CWERASE, VWERASE )
761 CI_ENTRY("lnext", CLNEXT, VLNEXT )
764 CI_ENTRY("flush", CFLUSHO, VFLUSHO )
767 CI_ENTRY("status", CSTATUS, VSTATUS )
769 /* These must be last because of the display routines */
770 CI_ENTRY("min", 1, VMIN )
771 CI_ENTRY("time", 0, VTIME )
775 NUM_control_info = ARRAY_SIZE(control_info)
780 const char *device_name;
781 /* The width of the screen, for output wrapping */
783 /* Current position, to know when to wrap */
784 unsigned current_col;
787 #define G (*(struct globals*)bb_common_bufsiz1)
788 #define INIT_G() do { \
789 G.device_name = bb_msg_standard_input; \
793 static void set_speed_or_die(enum speed_setting type, const char *arg,
794 struct termios *mode)
798 baud = tty_value_to_baud(xatou(arg));
800 if (type != output_speed) { /* either input or both */
801 cfsetispeed(mode, baud);
803 if (type != input_speed) { /* either output or both */
804 cfsetospeed(mode, baud);
808 static NORETURN void perror_on_device_and_die(const char *fmt)
810 bb_perror_msg_and_die(fmt, G.device_name);
813 static void perror_on_device(const char *fmt)
815 bb_perror_msg(fmt, G.device_name);
818 /* Print format string MESSAGE and optional args.
819 Wrap to next line first if it won't fit.
820 Print a space first unless MESSAGE will start a new line */
821 static void wrapf(const char *message, ...)
827 va_start(args, message);
828 buflen = vsnprintf(buf, sizeof(buf), message, args);
830 /* We seem to be called only with suitable lengths, but check if
831 somebody failed to adhere to this assumption just to be sure. */
832 if (!buflen || buflen >= sizeof(buf)) return;
834 if (G.current_col > 0) {
836 if (buf[0] != '\n') {
837 if (G.current_col + buflen >= G.max_col) {
845 G.current_col += buflen;
846 if (buf[buflen-1] == '\n')
850 static void newline(void)
852 if (G.current_col != 0)
857 static void set_window_size(int rows, int cols)
859 struct winsize win = { 0, 0, 0, 0 };
861 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
862 if (errno != EINVAL) {
865 memset(&win, 0, sizeof(win));
873 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
875 perror_on_device("%s");
879 static void display_window_size(int fancy)
881 const char *fmt_str = "%s\0%s: no size information for this device";
882 unsigned width, height;
884 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
885 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
886 perror_on_device(fmt_str);
889 wrapf(fancy ? "rows %u; columns %u;" : "%u %u\n",
894 static const struct suffix_mult stty_suffixes[] = {
901 static const struct mode_info *find_mode(const char *name)
903 int i = index_in_strings(mode_name, name);
904 return i >= 0 ? &mode_info[i] : NULL;
907 static const struct control_info *find_control(const char *name)
909 int i = index_in_strings(control_name, name);
910 return i >= 0 ? &control_info[i] : NULL;
914 param_need_arg = 0x80,
915 param_line = 1 | 0x80,
916 param_rows = 2 | 0x80,
917 param_cols = 3 | 0x80,
918 param_columns = 4 | 0x80,
921 param_ispeed = 7 | 0x80,
922 param_ospeed = 8 | 0x80,
925 static int find_param(const char *name)
927 static const char params[] ALIGN1 =
936 int i = index_in_strings(params, name) + 1;
939 if (i != 5 && i != 6)
944 static int recover_mode(const char *arg, struct termios *mode)
948 unsigned long iflag, oflag, cflag, lflag;
950 /* Scan into temporaries since it is too much trouble to figure out
951 the right format for 'tcflag_t' */
952 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
953 &iflag, &oflag, &cflag, &lflag, &n) != 4)
955 mode->c_iflag = iflag;
956 mode->c_oflag = oflag;
957 mode->c_cflag = cflag;
958 mode->c_lflag = lflag;
960 for (i = 0; i < NCCS; ++i) {
961 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
967 /* Fail if there are too many fields */
974 static void display_recoverable(const struct termios *mode,
975 int UNUSED_PARAM dummy)
978 printf("%lx:%lx:%lx:%lx",
979 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
980 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
981 for (i = 0; i < NCCS; ++i)
982 printf(":%x", (unsigned int) mode->c_cc[i]);
986 static void display_speed(const struct termios *mode, int fancy)
988 //____________________ 01234567 8 9
989 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
990 unsigned long ispeed, ospeed;
992 ispeed = cfgetispeed(mode);
993 ospeed = cfgetospeed(mode);
994 if (ispeed == 0 || ispeed == ospeed) {
995 ispeed = ospeed; /* in case ispeed was 0 */
996 //________ 0123 4 5 6 7 8 9
997 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
999 if (fancy) fmt_str += 9;
1000 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
1003 static void do_display(const struct termios *mode, int all)
1008 int prev_type = control;
1010 display_speed(mode, 1);
1012 display_window_size(1);
1014 wrapf("line = %u;\n", mode->c_line);
1019 for (i = 0; i != CIDX_min; ++i) {
1021 /* If swtch is the same as susp, don't print both */
1023 if (i == CIDX_swtch)
1026 /* If eof uses the same slot as min, only print whichever applies */
1028 if (!(mode->c_lflag & ICANON)
1029 && (i == CIDX_eof || i == CIDX_eol)
1034 ch = mode->c_cc[control_info[i].offset];
1035 if (ch == _POSIX_VDISABLE)
1036 strcpy(G.buf, "<undef>");
1038 visible(ch, G.buf, 0);
1039 wrapf("%s = %s;", nth_string(control_name, i), G.buf);
1042 if ((mode->c_lflag & ICANON) == 0)
1044 wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1047 for (i = 0; i < NUM_mode_info; ++i) {
1048 if (mode_info[i].flags & OMIT)
1050 if (mode_info[i].type != prev_type) {
1052 prev_type = mode_info[i].type;
1055 bitsp = get_ptr_to_tcflag(mode_info[i].type, mode);
1056 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1057 if ((*bitsp & mask) == mode_info[i].bits) {
1058 if (all || (mode_info[i].flags & SANE_UNSET))
1059 wrapf("-%s"+1, nth_string(mode_name, i));
1061 if ((all && mode_info[i].flags & REV)
1062 || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
1064 wrapf("-%s", nth_string(mode_name, i));
1071 static void sane_mode(struct termios *mode)
1075 for (i = 0; i < NUM_control_info; ++i) {
1080 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1083 for (i = 0; i < NUM_mode_info; ++i) {
1085 tcflag_t *bitsp = get_ptr_to_tcflag(mode_info[i].type, mode);
1089 val = *bitsp & ~((unsigned long)mode_info[i].mask);
1090 if (mode_info[i].flags & SANE_SET) {
1091 *bitsp = val | mode_info[i].bits;
1093 if (mode_info[i].flags & SANE_UNSET) {
1094 *bitsp = val & ~mode_info[i].bits;
1099 static void set_mode(const struct mode_info *info, int reversed,
1100 struct termios *mode)
1104 bitsp = get_ptr_to_tcflag(info->type, mode);
1107 tcflag_t val = *bitsp & ~info->mask;
1109 *bitsp = val & ~info->bits;
1111 *bitsp = val | info->bits;
1115 /* !bitsp - it's a "combination" mode */
1116 if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
1118 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1120 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
1121 } else if (info == &mode_info[IDX_oddp]) {
1123 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1125 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
1126 } else if (info == &mode_info[IDX_nl]) {
1128 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
1129 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
1131 mode->c_iflag = mode->c_iflag & ~ICRNL;
1132 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1134 } else if (info == &mode_info[IDX_ek]) {
1135 mode->c_cc[VERASE] = CERASE;
1136 mode->c_cc[VKILL] = CKILL;
1137 } else if (info == &mode_info[IDX_sane]) {
1139 } else if (info == &mode_info[IDX_cbreak]) {
1141 mode->c_lflag |= ICANON;
1143 mode->c_lflag &= ~ICANON;
1144 } else if (info == &mode_info[IDX_pass8]) {
1146 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1147 mode->c_iflag |= ISTRIP;
1149 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1150 mode->c_iflag &= ~ISTRIP;
1152 } else if (info == &mode_info[IDX_litout]) {
1154 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1155 mode->c_iflag |= ISTRIP;
1156 mode->c_oflag |= OPOST;
1158 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1159 mode->c_iflag &= ~ISTRIP;
1160 mode->c_oflag &= ~OPOST;
1162 } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
1163 if ((info == &mode_info[IDX_raw] && reversed)
1164 || (info == &mode_info[IDX_cooked] && !reversed)
1167 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1168 mode->c_oflag |= OPOST;
1169 mode->c_lflag |= ISIG | ICANON;
1171 mode->c_cc[VEOF] = CEOF;
1174 mode->c_cc[VEOL] = CEOL;
1179 mode->c_oflag &= ~OPOST;
1180 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1181 mode->c_cc[VMIN] = 1;
1182 mode->c_cc[VTIME] = 0;
1186 else if (info == &mode_info[IDX_decctlq]) {
1188 mode->c_iflag |= IXANY;
1190 mode->c_iflag &= ~IXANY;
1194 else if (info == &mode_info[IDX_tabs]) {
1196 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1198 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1202 else if (info == &mode_info[IDX_tabs]) {
1204 mode->c_oflag |= OXTABS;
1206 mode->c_oflag &= ~OXTABS;
1209 #if XCASE && IUCLC && OLCUC
1210 else if (info==&mode_info[IDX_lcase] || info==&mode_info[IDX_LCASE]) {
1212 mode->c_lflag &= ~XCASE;
1213 mode->c_iflag &= ~IUCLC;
1214 mode->c_oflag &= ~OLCUC;
1216 mode->c_lflag |= XCASE;
1217 mode->c_iflag |= IUCLC;
1218 mode->c_oflag |= OLCUC;
1222 else if (info == &mode_info[IDX_crt]) {
1223 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1224 } else if (info == &mode_info[IDX_dec]) {
1225 mode->c_cc[VINTR] = 3; /* ^C */
1226 mode->c_cc[VERASE] = 127; /* DEL */
1227 mode->c_cc[VKILL] = 21; /* ^U */
1228 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1229 if (IXANY) mode->c_iflag &= ~IXANY;
1233 static void set_control_char_or_die(const struct control_info *info,
1234 const char *arg, struct termios *mode)
1236 unsigned char value;
1238 if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
1239 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1240 else if (arg[0] == '\0' || arg[1] == '\0')
1242 else if (strcmp(arg, "^-") == 0 || strcmp(arg, "undef") == 0)
1243 value = _POSIX_VDISABLE;
1244 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1245 value = arg[1] & 0x1f; /* Non-letters get weird results */
1249 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1250 mode->c_cc[info->offset] = value;
1253 #define STTY_require_set_attr (1 << 0)
1254 #define STTY_speed_was_set (1 << 1)
1255 #define STTY_verbose_output (1 << 2)
1256 #define STTY_recoverable_output (1 << 3)
1257 #define STTY_noargs (1 << 4)
1259 int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1260 int stty_main(int argc UNUSED_PARAM, char **argv)
1262 struct termios mode;
1263 void (*output_func)(const struct termios *, int);
1264 const char *file_name = NULL;
1265 int display_all = 0;
1271 stty_state = STTY_noargs;
1272 output_func = do_display;
1274 /* First pass: only parse/verify command line params */
1277 const struct mode_info *mp;
1278 const struct control_info *cp;
1279 const char *arg = argv[k];
1280 const char *argnext = argv[k+1];
1283 if (arg[0] == '-') {
1285 mp = find_mode(arg+1);
1287 if (!(mp->flags & REV))
1288 goto invalid_argument;
1289 stty_state &= ~STTY_noargs;
1292 /* It is an option - parse it */
1297 stty_state |= STTY_verbose_output;
1298 output_func = do_display;
1302 stty_state |= STTY_recoverable_output;
1303 output_func = display_recoverable;
1307 bb_error_msg_and_die("only one device may be specified");
1308 file_name = &arg[i+1]; /* "-Fdevice" ? */
1309 if (!file_name[0]) { /* nope, "-F device" */
1310 int p = k+1; /* argv[p] is argnext */
1311 file_name = argnext;
1313 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1314 /* remove -F param from arg[vc] */
1316 argv[p] = argv[p+1];
1322 goto invalid_argument;
1329 mp = find_mode(arg);
1331 stty_state &= ~STTY_noargs;
1335 cp = find_control(arg);
1338 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1339 /* called for the side effect of xfunc death only */
1340 set_control_char_or_die(cp, argnext, &mode);
1341 stty_state &= ~STTY_noargs;
1346 param = find_param(arg);
1347 if (param & param_need_arg) {
1349 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1357 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1359 # endif /* else fall-through */
1365 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1372 /* called for the side effect of xfunc death only */
1373 set_speed_or_die(input_speed, argnext, &mode);
1376 /* called for the side effect of xfunc death only */
1377 set_speed_or_die(output_speed, argnext, &mode);
1380 if (recover_mode(arg, &mode) == 1) break;
1381 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1383 bb_error_msg_and_die("invalid argument '%s'", arg);
1385 stty_state &= ~STTY_noargs;
1388 /* Specifying both -a and -g is an error */
1389 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1390 (STTY_verbose_output | STTY_recoverable_output)
1392 bb_error_msg_and_die("-a and -g are mutually exclusive");
1394 /* Specifying -a or -g with non-options is an error */
1395 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output))
1396 && !(stty_state & STTY_noargs)
1398 bb_error_msg_and_die("modes may not be set when -a or -g is used");
1401 /* Now it is safe to start doing things */
1403 G.device_name = file_name;
1404 xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO);
1405 ndelay_off(STDIN_FILENO);
1408 /* Initialize to all zeroes so there is no risk memcmp will report a
1409 spurious difference in an uninitialized portion of the structure */
1410 memset(&mode, 0, sizeof(mode));
1411 if (tcgetattr(STDIN_FILENO, &mode))
1412 perror_on_device_and_die("%s");
1414 if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
1415 G.max_col = get_terminal_width(STDOUT_FILENO);
1416 output_func(&mode, display_all);
1417 return EXIT_SUCCESS;
1420 /* Second pass: perform actions */
1423 const struct mode_info *mp;
1424 const struct control_info *cp;
1425 const char *arg = argv[k];
1426 const char *argnext = argv[k+1];
1429 if (arg[0] == '-') {
1430 mp = find_mode(arg+1);
1432 set_mode(mp, 1 /* reversed */, &mode);
1433 stty_state |= STTY_require_set_attr;
1435 /* It is an option - already parsed. Skip it */
1439 mp = find_mode(arg);
1441 set_mode(mp, 0 /* non-reversed */, &mode);
1442 stty_state |= STTY_require_set_attr;
1446 cp = find_control(arg);
1449 set_control_char_or_die(cp, argnext, &mode);
1450 stty_state |= STTY_require_set_attr;
1454 param = find_param(arg);
1455 if (param & param_need_arg) {
1462 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1463 stty_state |= STTY_require_set_attr;
1469 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1472 display_window_size(0);
1475 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1479 display_speed(&mode, 0);
1482 set_speed_or_die(input_speed, argnext, &mode);
1483 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1486 set_speed_or_die(output_speed, argnext, &mode);
1487 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1490 if (recover_mode(arg, &mode) == 1)
1491 stty_state |= STTY_require_set_attr;
1492 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
1493 set_speed_or_die(both_speeds, arg, &mode);
1494 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1495 } /* else - impossible (caught in the first pass):
1496 bb_error_msg_and_die("invalid argument '%s'", arg); */
1500 if (stty_state & STTY_require_set_attr) {
1501 struct termios new_mode;
1503 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1504 perror_on_device_and_die("%s");
1506 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1507 it performs *any* of the requested operations. This means it
1508 can report 'success' when it has actually failed to perform
1509 some proper subset of the requested operations. To detect
1510 this partial failure, get the current terminal attributes and
1511 compare them to the requested ones */
1513 /* Initialize to all zeroes so there is no risk memcmp will report a
1514 spurious difference in an uninitialized portion of the structure */
1515 memset(&new_mode, 0, sizeof(new_mode));
1516 if (tcgetattr(STDIN_FILENO, &new_mode))
1517 perror_on_device_and_die("%s");
1519 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1521 * I think the below chunk is not necessary on Linux.
1522 * If you are deleting it, also delete STTY_speed_was_set bit -
1523 * it is only ever checked here.
1525 #if 0 /* was "if CIBAUD" */
1526 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1527 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1528 sometimes (m1 != m2). The only difference is in the four bits
1529 of the c_cflag field corresponding to the baud rate. To save
1530 Sun users a little confusion, don't report an error if this
1531 happens. But suppress the error only if we haven't tried to
1532 set the baud rate explicitly -- otherwise we'd never give an
1533 error for a true failure to set the baud rate */
1535 new_mode.c_cflag &= (~CIBAUD);
1536 if ((stty_state & STTY_speed_was_set)
1537 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1539 perror_on_device_and_die("%s: cannot perform all requested operations");
1543 return EXIT_SUCCESS;