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_NOEXEC(stty, stty, BB_DIR_BIN, BB_SUID_DROP, stty))
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;
786 #define G (*(struct globals*)bb_common_bufsiz1)
787 #define INIT_G() do { \
788 G.device_name = bb_msg_standard_input; \
790 G.current_col = 0; /* we are noexec, must clear */ \
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) {
1023 /* If swtch is the same as susp, don't print both */
1025 if (i == CIDX_swtch)
1028 /* If eof uses the same slot as min, only print whichever applies */
1030 if (!(mode->c_lflag & ICANON)
1031 && (i == CIDX_eof || i == CIDX_eol)
1036 ch = mode->c_cc[control_info[i].offset];
1037 if (ch == _POSIX_VDISABLE)
1038 strcpy(buf10, "<undef>");
1040 visible(ch, buf10, 0);
1041 wrapf("%s = %s;", nth_string(control_name, i), buf10);
1044 if ((mode->c_lflag & ICANON) == 0)
1046 wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1049 for (i = 0; i < NUM_mode_info; ++i) {
1050 if (mode_info[i].flags & OMIT)
1052 if (mode_info[i].type != prev_type) {
1054 prev_type = mode_info[i].type;
1057 bitsp = get_ptr_to_tcflag(mode_info[i].type, mode);
1058 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1059 if ((*bitsp & mask) == mode_info[i].bits) {
1060 if (all || (mode_info[i].flags & SANE_UNSET))
1061 wrapf("-%s"+1, nth_string(mode_name, i));
1063 if ((all && mode_info[i].flags & REV)
1064 || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
1066 wrapf("-%s", nth_string(mode_name, i));
1073 static void sane_mode(struct termios *mode)
1077 for (i = 0; i < NUM_control_info; ++i) {
1082 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1085 for (i = 0; i < NUM_mode_info; ++i) {
1087 tcflag_t *bitsp = get_ptr_to_tcflag(mode_info[i].type, mode);
1091 val = *bitsp & ~((unsigned long)mode_info[i].mask);
1092 if (mode_info[i].flags & SANE_SET) {
1093 *bitsp = val | mode_info[i].bits;
1095 if (mode_info[i].flags & SANE_UNSET) {
1096 *bitsp = val & ~mode_info[i].bits;
1101 static void set_mode(const struct mode_info *info, int reversed,
1102 struct termios *mode)
1106 bitsp = get_ptr_to_tcflag(info->type, mode);
1109 tcflag_t val = *bitsp & ~info->mask;
1111 *bitsp = val & ~info->bits;
1113 *bitsp = val | info->bits;
1117 /* !bitsp - it's a "combination" mode */
1118 if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
1120 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1122 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
1123 } else if (info == &mode_info[IDX_oddp]) {
1125 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1127 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
1128 } else if (info == &mode_info[IDX_nl]) {
1130 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
1131 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
1133 mode->c_iflag = mode->c_iflag & ~ICRNL;
1134 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1136 } else if (info == &mode_info[IDX_ek]) {
1137 mode->c_cc[VERASE] = CERASE;
1138 mode->c_cc[VKILL] = CKILL;
1139 } else if (info == &mode_info[IDX_sane]) {
1141 } else if (info == &mode_info[IDX_cbreak]) {
1143 mode->c_lflag |= ICANON;
1145 mode->c_lflag &= ~ICANON;
1146 } else if (info == &mode_info[IDX_pass8]) {
1148 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1149 mode->c_iflag |= ISTRIP;
1151 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1152 mode->c_iflag &= ~ISTRIP;
1154 } else if (info == &mode_info[IDX_litout]) {
1156 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1157 mode->c_iflag |= ISTRIP;
1158 mode->c_oflag |= OPOST;
1160 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1161 mode->c_iflag &= ~ISTRIP;
1162 mode->c_oflag &= ~OPOST;
1164 } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
1165 if ((info == &mode_info[IDX_raw] && reversed)
1166 || (info == &mode_info[IDX_cooked] && !reversed)
1169 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1170 mode->c_oflag |= OPOST;
1171 mode->c_lflag |= ISIG | ICANON;
1173 mode->c_cc[VEOF] = CEOF;
1176 mode->c_cc[VEOL] = CEOL;
1181 mode->c_oflag &= ~OPOST;
1182 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1183 mode->c_cc[VMIN] = 1;
1184 mode->c_cc[VTIME] = 0;
1188 else if (info == &mode_info[IDX_decctlq]) {
1190 mode->c_iflag |= IXANY;
1192 mode->c_iflag &= ~IXANY;
1196 else if (info == &mode_info[IDX_tabs]) {
1198 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1200 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1204 else if (info == &mode_info[IDX_tabs]) {
1206 mode->c_oflag |= OXTABS;
1208 mode->c_oflag &= ~OXTABS;
1211 #if XCASE && IUCLC && OLCUC
1212 else if (info==&mode_info[IDX_lcase] || info==&mode_info[IDX_LCASE]) {
1214 mode->c_lflag &= ~XCASE;
1215 mode->c_iflag &= ~IUCLC;
1216 mode->c_oflag &= ~OLCUC;
1218 mode->c_lflag |= XCASE;
1219 mode->c_iflag |= IUCLC;
1220 mode->c_oflag |= OLCUC;
1224 else if (info == &mode_info[IDX_crt]) {
1225 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1226 } else if (info == &mode_info[IDX_dec]) {
1227 mode->c_cc[VINTR] = 3; /* ^C */
1228 mode->c_cc[VERASE] = 127; /* DEL */
1229 mode->c_cc[VKILL] = 21; /* ^U */
1230 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1231 if (IXANY) mode->c_iflag &= ~IXANY;
1235 static void set_control_char_or_die(const struct control_info *info,
1236 const char *arg, struct termios *mode)
1238 unsigned char value;
1240 if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
1241 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1242 else if (arg[0] == '\0' || arg[1] == '\0')
1244 else if (strcmp(arg, "^-") == 0 || strcmp(arg, "undef") == 0)
1245 value = _POSIX_VDISABLE;
1246 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1247 value = arg[1] & 0x1f; /* Non-letters get weird results */
1251 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1252 mode->c_cc[info->offset] = value;
1255 #define STTY_require_set_attr (1 << 0)
1256 #define STTY_speed_was_set (1 << 1)
1257 #define STTY_verbose_output (1 << 2)
1258 #define STTY_recoverable_output (1 << 3)
1259 #define STTY_noargs (1 << 4)
1261 int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1262 int stty_main(int argc UNUSED_PARAM, char **argv)
1264 struct termios mode;
1265 void (*output_func)(const struct termios *, int);
1266 const char *file_name = NULL;
1267 int display_all = 0;
1273 stty_state = STTY_noargs;
1274 output_func = do_display;
1276 /* First pass: only parse/verify command line params */
1279 const struct mode_info *mp;
1280 const struct control_info *cp;
1281 const char *arg = argv[k];
1282 const char *argnext = argv[k+1];
1285 if (arg[0] == '-') {
1287 mp = find_mode(arg+1);
1289 if (!(mp->flags & REV))
1290 goto invalid_argument;
1291 stty_state &= ~STTY_noargs;
1294 /* It is an option - parse it */
1299 stty_state |= STTY_verbose_output;
1300 output_func = do_display;
1304 stty_state |= STTY_recoverable_output;
1305 output_func = display_recoverable;
1309 bb_error_msg_and_die("only one device may be specified");
1310 file_name = &arg[i+1]; /* "-Fdevice" ? */
1311 if (!file_name[0]) { /* nope, "-F device" */
1312 int p = k+1; /* argv[p] is argnext */
1313 file_name = argnext;
1315 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1316 /* remove -F param from arg[vc] */
1318 argv[p] = argv[p+1];
1324 goto invalid_argument;
1331 mp = find_mode(arg);
1333 stty_state &= ~STTY_noargs;
1337 cp = find_control(arg);
1340 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1341 /* called for the side effect of xfunc death only */
1342 set_control_char_or_die(cp, argnext, &mode);
1343 stty_state &= ~STTY_noargs;
1348 param = find_param(arg);
1349 if (param & param_need_arg) {
1351 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1359 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1361 # endif /* else fall-through */
1367 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1374 /* called for the side effect of xfunc death only */
1375 set_speed_or_die(input_speed, argnext, &mode);
1378 /* called for the side effect of xfunc death only */
1379 set_speed_or_die(output_speed, argnext, &mode);
1382 if (recover_mode(arg, &mode) == 1) break;
1383 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1385 bb_error_msg_and_die("invalid argument '%s'", arg);
1387 stty_state &= ~STTY_noargs;
1390 /* Specifying both -a and -g is an error */
1391 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1392 (STTY_verbose_output | STTY_recoverable_output)
1394 bb_error_msg_and_die("-a and -g are mutually exclusive");
1396 /* Specifying -a or -g with non-options is an error */
1397 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output))
1398 && !(stty_state & STTY_noargs)
1400 bb_error_msg_and_die("modes may not be set when -a or -g is used");
1403 /* Now it is safe to start doing things */
1405 G.device_name = file_name;
1406 xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO);
1407 ndelay_off(STDIN_FILENO);
1410 /* Initialize to all zeroes so there is no risk memcmp will report a
1411 spurious difference in an uninitialized portion of the structure */
1412 memset(&mode, 0, sizeof(mode));
1413 if (tcgetattr(STDIN_FILENO, &mode))
1414 perror_on_device_and_die("%s");
1416 if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
1417 G.max_col = get_terminal_width(STDOUT_FILENO);
1418 output_func(&mode, display_all);
1419 return EXIT_SUCCESS;
1422 /* Second pass: perform actions */
1425 const struct mode_info *mp;
1426 const struct control_info *cp;
1427 const char *arg = argv[k];
1428 const char *argnext = argv[k+1];
1431 if (arg[0] == '-') {
1432 mp = find_mode(arg+1);
1434 set_mode(mp, 1 /* reversed */, &mode);
1435 stty_state |= STTY_require_set_attr;
1437 /* It is an option - already parsed. Skip it */
1441 mp = find_mode(arg);
1443 set_mode(mp, 0 /* non-reversed */, &mode);
1444 stty_state |= STTY_require_set_attr;
1448 cp = find_control(arg);
1451 set_control_char_or_die(cp, argnext, &mode);
1452 stty_state |= STTY_require_set_attr;
1456 param = find_param(arg);
1457 if (param & param_need_arg) {
1464 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1465 stty_state |= STTY_require_set_attr;
1471 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1474 display_window_size(0);
1477 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1481 display_speed(&mode, 0);
1484 set_speed_or_die(input_speed, argnext, &mode);
1485 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1488 set_speed_or_die(output_speed, argnext, &mode);
1489 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1492 if (recover_mode(arg, &mode) == 1)
1493 stty_state |= STTY_require_set_attr;
1494 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
1495 set_speed_or_die(both_speeds, arg, &mode);
1496 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1497 } /* else - impossible (caught in the first pass):
1498 bb_error_msg_and_die("invalid argument '%s'", arg); */
1502 if (stty_state & STTY_require_set_attr) {
1503 struct termios new_mode;
1505 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1506 perror_on_device_and_die("%s");
1508 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1509 it performs *any* of the requested operations. This means it
1510 can report 'success' when it has actually failed to perform
1511 some proper subset of the requested operations. To detect
1512 this partial failure, get the current terminal attributes and
1513 compare them to the requested ones */
1515 /* Initialize to all zeroes so there is no risk memcmp will report a
1516 spurious difference in an uninitialized portion of the structure */
1517 memset(&new_mode, 0, sizeof(new_mode));
1518 if (tcgetattr(STDIN_FILENO, &new_mode))
1519 perror_on_device_and_die("%s");
1521 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1523 * I think the below chunk is not necessary on Linux.
1524 * If you are deleting it, also delete STTY_speed_was_set bit -
1525 * it is only ever checked here.
1527 #if 0 /* was "if CIBAUD" */
1528 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1529 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1530 sometimes (m1 != m2). The only difference is in the four bits
1531 of the c_cflag field corresponding to the baud rate. To save
1532 Sun users a little confusion, don't report an error if this
1533 happens. But suppress the error only if we haven't tried to
1534 set the baud rate explicitly -- otherwise we'd never give an
1535 error for a true failure to set the baud rate */
1537 new_mode.c_cflag &= (~CIBAUD);
1538 if ((stty_state & STTY_speed_was_set)
1539 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1541 perror_on_device_and_die("%s: cannot perform all requested operations");
1545 return EXIT_SUCCESS;