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 */
254 /* Which speeds to set */
256 input_speed, output_speed, both_speeds
259 /* Which member(s) of 'struct termios' a mode uses */
261 control, input, output, local, combination
263 static tcflag_t *get_ptr_to_tcflag(unsigned type, const struct termios *mode)
265 static const uint8_t tcflag_offsets[] ALIGN1 = {
266 offsetof(struct termios, c_cflag), /* control */
267 offsetof(struct termios, c_iflag), /* input */
268 offsetof(struct termios, c_oflag), /* output */
269 offsetof(struct termios, c_lflag) /* local */
272 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
277 /* Flags for 'struct mode_info' */
278 #define SANE_SET 1 /* Set in 'sane' mode */
279 #define SANE_UNSET 2 /* Unset in 'sane' mode */
280 #define REV 4 /* Can be turned off by prepending '-' */
281 #define OMIT 8 /* Don't display value */
285 * This structure should be kept as small as humanly possible.
288 const uint8_t type; /* Which structure element to change */
289 const uint8_t flags; /* Setting and display options */
290 /* only these values are ever used, so... */
291 #if (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100
293 #elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000
296 const tcflag_t mask; /* Other bits to turn off for this mode */
298 /* was using short here, but ppc32 was unhappy */
299 const tcflag_t bits; /* Bits to set for this mode */
303 /* Must match mode_name[] and mode_info[] order! */
323 #if XCASE && IUCLC && OLCUC
329 #define MI_ENTRY(N,T,F,B,M) N "\0"
331 /* Mode names given on command line */
332 static const char mode_name[] ALIGN1 =
333 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
334 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
335 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
336 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
337 MI_ENTRY("ek", combination, OMIT, 0, 0 )
338 MI_ENTRY("sane", combination, OMIT, 0, 0 )
339 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
340 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
341 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
342 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
343 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
344 MI_ENTRY("crt", combination, OMIT, 0, 0 )
345 MI_ENTRY("dec", combination, OMIT, 0, 0 )
347 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
350 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
352 #if XCASE && IUCLC && OLCUC
353 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 )
354 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 )
356 MI_ENTRY("parenb", control, REV, PARENB, 0 )
357 MI_ENTRY("parodd", control, REV, PARODD, 0 )
359 MI_ENTRY("cmspar", control, REV, CMSPAR, 0 )
361 MI_ENTRY("cs5", control, 0, CS5, CSIZE)
362 MI_ENTRY("cs6", control, 0, CS6, CSIZE)
363 MI_ENTRY("cs7", control, 0, CS7, CSIZE)
364 MI_ENTRY("cs8", control, 0, CS8, CSIZE)
365 MI_ENTRY("hupcl", control, REV, HUPCL, 0 )
366 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 )
367 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 )
368 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 )
369 MI_ENTRY("clocal", control, REV, CLOCAL, 0 )
371 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
373 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 )
374 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 )
375 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 )
376 MI_ENTRY("parmrk", input, REV, PARMRK, 0 )
377 MI_ENTRY("inpck", input, REV, INPCK, 0 )
378 MI_ENTRY("istrip", input, REV, ISTRIP, 0 )
379 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 )
380 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 )
381 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
382 MI_ENTRY("ixon", input, REV, IXON, 0 )
383 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
384 MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 )
386 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
389 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
392 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
395 MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 )
397 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
399 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
402 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
405 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
408 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
411 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
414 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
417 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
420 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
421 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
424 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY)
425 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY)
426 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY)
427 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY)
431 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY)
433 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY)
436 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY)
438 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY)
441 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
446 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
447 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
450 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
451 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
454 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
455 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
457 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
458 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
460 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
462 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
463 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
464 MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 )
465 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
466 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
467 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
469 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
472 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
475 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
476 MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 )
479 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
480 MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 )
483 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
484 MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 )
486 MI_ENTRY("flusho", local, SANE_UNSET | REV, FLUSHO, 0 )
488 MI_ENTRY("extproc", local, SANE_UNSET | REV, EXTPROC, 0 )
493 #define MI_ENTRY(N,T,F,B,M) { T, F, M, B },
495 static const struct mode_info mode_info[] = {
496 /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */
497 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
498 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
499 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
500 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
501 MI_ENTRY("ek", combination, OMIT, 0, 0 )
502 MI_ENTRY("sane", combination, OMIT, 0, 0 )
503 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
504 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
505 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
506 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
507 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
508 MI_ENTRY("crt", combination, OMIT, 0, 0 )
509 MI_ENTRY("dec", combination, OMIT, 0, 0 )
511 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
514 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
516 #if XCASE && IUCLC && OLCUC
517 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 )
518 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 )
520 MI_ENTRY("parenb", control, REV, PARENB, 0 )
521 MI_ENTRY("parodd", control, REV, PARODD, 0 )
523 MI_ENTRY("cmspar", control, REV, CMSPAR, 0 )
525 MI_ENTRY("cs5", control, 0, CS5, CSIZE)
526 MI_ENTRY("cs6", control, 0, CS6, CSIZE)
527 MI_ENTRY("cs7", control, 0, CS7, CSIZE)
528 MI_ENTRY("cs8", control, 0, CS8, CSIZE)
529 MI_ENTRY("hupcl", control, REV, HUPCL, 0 )
530 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 )
531 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 )
532 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 )
533 MI_ENTRY("clocal", control, REV, CLOCAL, 0 )
535 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
537 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 )
538 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 )
539 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 )
540 MI_ENTRY("parmrk", input, REV, PARMRK, 0 )
541 MI_ENTRY("inpck", input, REV, INPCK, 0 )
542 MI_ENTRY("istrip", input, REV, ISTRIP, 0 )
543 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 )
544 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 )
545 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
546 MI_ENTRY("ixon", input, REV, IXON, 0 )
547 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
548 MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 )
550 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
553 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
556 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
559 MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 )
561 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
563 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
566 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
569 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
572 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
575 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
578 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
581 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
584 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
585 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
588 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY)
589 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY)
590 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY)
591 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY)
595 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY)
597 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY)
600 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY)
602 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY)
605 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
610 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
611 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
614 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
615 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
618 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
619 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
621 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
622 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
624 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
626 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
627 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
628 MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 )
629 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
630 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
631 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
633 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
636 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
639 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
640 MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 )
643 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
644 MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 )
647 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
648 MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 )
650 MI_ENTRY("flusho", local, SANE_UNSET | REV, FLUSHO, 0 )
652 MI_ENTRY("extproc", local, SANE_UNSET | REV, EXTPROC, 0 )
657 NUM_mode_info = ARRAY_SIZE(mode_info)
661 /* Control characters */
662 struct control_info {
663 const uint8_t saneval; /* Value to set for 'stty sane' */
664 const uint8_t offset; /* Offset in c_cc */
668 /* Must match control_name[] and control_info[] order! */
706 #define CI_ENTRY(n,s,o) n "\0"
708 /* Name given on command line */
709 static const char control_name[] ALIGN1 =
710 CI_ENTRY("intr", CINTR, VINTR )
711 CI_ENTRY("quit", CQUIT, VQUIT )
712 CI_ENTRY("erase", CERASE, VERASE )
713 CI_ENTRY("kill", CKILL, VKILL )
714 CI_ENTRY("eof", CEOF, VEOF )
715 CI_ENTRY("eol", CEOL, VEOL )
717 CI_ENTRY("eol2", CEOL2, VEOL2 )
720 CI_ENTRY("swtch", CSWTCH, VSWTCH )
722 CI_ENTRY("start", CSTART, VSTART )
723 CI_ENTRY("stop", CSTOP, VSTOP )
724 CI_ENTRY("susp", CSUSP, VSUSP )
726 CI_ENTRY("dsusp", CDSUSP, VDSUSP )
729 CI_ENTRY("rprnt", CRPRNT, VREPRINT)
732 CI_ENTRY("werase", CWERASE, VWERASE )
735 CI_ENTRY("lnext", CLNEXT, VLNEXT )
738 CI_ENTRY("flush", CFLUSHO, VFLUSHO )
741 CI_ENTRY("status", CSTATUS, VSTATUS )
743 /* These must be last because of the display routines */
744 CI_ENTRY("min", 1, VMIN )
745 CI_ENTRY("time", 0, VTIME )
749 #define CI_ENTRY(n,s,o) { s, o },
751 static const struct control_info control_info[] ALIGN2 = {
752 /* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */
753 CI_ENTRY("intr", CINTR, VINTR )
754 CI_ENTRY("quit", CQUIT, VQUIT )
755 CI_ENTRY("erase", CERASE, VERASE )
756 CI_ENTRY("kill", CKILL, VKILL )
757 CI_ENTRY("eof", CEOF, VEOF )
758 CI_ENTRY("eol", CEOL, VEOL )
760 CI_ENTRY("eol2", CEOL2, VEOL2 )
763 CI_ENTRY("swtch", CSWTCH, VSWTCH )
765 CI_ENTRY("start", CSTART, VSTART )
766 CI_ENTRY("stop", CSTOP, VSTOP )
767 CI_ENTRY("susp", CSUSP, VSUSP )
769 CI_ENTRY("dsusp", CDSUSP, VDSUSP )
772 CI_ENTRY("rprnt", CRPRNT, VREPRINT)
775 CI_ENTRY("werase", CWERASE, VWERASE )
778 CI_ENTRY("lnext", CLNEXT, VLNEXT )
781 CI_ENTRY("flush", CFLUSHO, VFLUSHO )
784 CI_ENTRY("status", CSTATUS, VSTATUS )
786 /* These must be last because of the display routines */
787 CI_ENTRY("min", 1, VMIN )
788 CI_ENTRY("time", 0, VTIME )
792 NUM_control_info = ARRAY_SIZE(control_info)
797 const char *device_name;
798 /* The width of the screen, for output wrapping */
800 /* Current position, to know when to wrap */
801 unsigned current_col;
803 #define G (*(struct globals*)bb_common_bufsiz1)
804 #define INIT_G() do { \
805 setup_common_bufsiz(); \
806 G.device_name = bb_msg_standard_input; \
808 G.current_col = 0; /* we are noexec, must clear */ \
811 static void set_speed_or_die(enum speed_setting type, const char *arg,
812 struct termios *mode)
816 baud = tty_value_to_baud(xatou(arg));
818 if (type != output_speed) { /* either input or both */
819 cfsetispeed(mode, baud);
821 if (type != input_speed) { /* either output or both */
822 cfsetospeed(mode, baud);
826 static NORETURN void perror_on_device_and_die(const char *fmt)
828 bb_perror_msg_and_die(fmt, G.device_name);
831 static void perror_on_device(const char *fmt)
833 bb_perror_msg(fmt, G.device_name);
836 /* Print format string MESSAGE and optional args.
837 Wrap to next line first if it won't fit.
838 Print a space first unless MESSAGE will start a new line */
839 static void wrapf(const char *message, ...)
845 va_start(args, message);
846 buflen = vsnprintf(buf, sizeof(buf), message, args);
848 /* We seem to be called only with suitable lengths, but check if
849 somebody failed to adhere to this assumption just to be sure. */
850 if (!buflen || buflen >= sizeof(buf)) return;
852 if (G.current_col > 0) {
854 if (buf[0] != '\n') {
855 if (G.current_col + buflen >= G.max_col) {
864 G.current_col += buflen;
865 if (buf[buflen-1] == '\n')
869 static void newline(void)
871 if (G.current_col != 0)
876 static void set_window_size(int rows, int cols)
878 struct winsize win = { 0, 0, 0, 0 };
880 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
881 if (errno != EINVAL) {
884 memset(&win, 0, sizeof(win));
892 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
894 perror_on_device("%s");
898 static void display_window_size(int fancy)
900 const char *fmt_str = "%s\0%s: no size information for this device";
901 unsigned width, height;
903 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
904 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
905 perror_on_device(fmt_str);
908 wrapf(fancy ? "rows %u; columns %u;" : "%u %u\n",
913 static const struct suffix_mult stty_suffixes[] = {
920 static const struct mode_info *find_mode(const char *name)
922 int i = index_in_strings(mode_name, name);
923 return i >= 0 ? &mode_info[i] : NULL;
926 static const struct control_info *find_control(const char *name)
928 int i = index_in_strings(control_name, name);
929 return i >= 0 ? &control_info[i] : NULL;
933 param_need_arg = 0x80,
934 param_line = 1 | 0x80,
935 param_rows = 2 | 0x80,
936 param_cols = 3 | 0x80,
937 param_columns = 4 | 0x80,
940 param_ispeed = 7 | 0x80,
941 param_ospeed = 8 | 0x80,
944 static int find_param(const char *name)
946 static const char params[] ALIGN1 =
955 int i = index_in_strings(params, name) + 1;
958 if (i != 5 && i != 6)
963 static int recover_mode(const char *arg, struct termios *mode)
967 unsigned long iflag, oflag, cflag, lflag;
969 /* Scan into temporaries since it is too much trouble to figure out
970 the right format for 'tcflag_t' */
971 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
972 &iflag, &oflag, &cflag, &lflag, &n) != 4)
974 mode->c_iflag = iflag;
975 mode->c_oflag = oflag;
976 mode->c_cflag = cflag;
977 mode->c_lflag = lflag;
979 for (i = 0; i < NCCS; ++i) {
980 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
986 /* Fail if there are too many fields */
993 static void display_recoverable(const struct termios *mode,
994 int UNUSED_PARAM dummy)
997 printf("%lx:%lx:%lx:%lx",
998 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
999 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1000 for (i = 0; i < NCCS; ++i)
1001 printf(":%x", (unsigned int) mode->c_cc[i]);
1005 static void display_speed(const struct termios *mode, int fancy)
1007 //____________________ 01234567 8 9
1008 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
1009 unsigned long ispeed, ospeed;
1011 ispeed = cfgetispeed(mode);
1012 ospeed = cfgetospeed(mode);
1013 if (ispeed == 0 || ispeed == ospeed) {
1014 ispeed = ospeed; /* in case ispeed was 0 */
1015 //________ 0123 4 5 6 7 8 9
1016 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
1018 if (fancy) fmt_str += 9;
1019 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
1022 static void do_display(const struct termios *mode, int all)
1027 int prev_type = control;
1029 display_speed(mode, 1);
1031 display_window_size(1);
1033 wrapf("line = %u;\n", mode->c_line);
1038 for (i = 0; i != CIDX_min; ++i) {
1042 /* If swtch is the same as susp, don't print both */
1044 if (i == CIDX_swtch)
1047 /* If eof uses the same slot as min, only print whichever applies */
1049 if (!(mode->c_lflag & ICANON)
1050 && (i == CIDX_eof || i == CIDX_eol)
1055 ch = mode->c_cc[control_info[i].offset];
1056 if (ch == _POSIX_VDISABLE)
1057 strcpy(buf10, "<undef>");
1059 visible(ch, buf10, 0);
1060 wrapf("%s = %s;", nth_string(control_name, i), buf10);
1063 if ((mode->c_lflag & ICANON) == 0)
1065 wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1068 for (i = 0; i < NUM_mode_info; ++i) {
1069 if (mode_info[i].flags & OMIT)
1071 if (mode_info[i].type != prev_type) {
1073 prev_type = mode_info[i].type;
1076 bitsp = get_ptr_to_tcflag(mode_info[i].type, mode);
1077 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1078 if ((*bitsp & mask) == mode_info[i].bits) {
1079 if (all || (mode_info[i].flags & SANE_UNSET))
1080 wrapf("-%s"+1, nth_string(mode_name, i));
1082 if ((all && mode_info[i].flags & REV)
1083 || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
1085 wrapf("-%s", nth_string(mode_name, i));
1092 static void sane_mode(struct termios *mode)
1096 for (i = 0; i < NUM_control_info; ++i) {
1101 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1104 for (i = 0; i < NUM_mode_info; ++i) {
1106 tcflag_t *bitsp = get_ptr_to_tcflag(mode_info[i].type, mode);
1110 val = *bitsp & ~((unsigned long)mode_info[i].mask);
1111 if (mode_info[i].flags & SANE_SET) {
1112 *bitsp = val | mode_info[i].bits;
1114 if (mode_info[i].flags & SANE_UNSET) {
1115 *bitsp = val & ~mode_info[i].bits;
1120 static void set_mode(const struct mode_info *info, int reversed,
1121 struct termios *mode)
1125 bitsp = get_ptr_to_tcflag(info->type, mode);
1128 tcflag_t val = *bitsp & ~info->mask;
1130 *bitsp = val & ~info->bits;
1132 *bitsp = val | info->bits;
1136 /* !bitsp - it's a "combination" mode */
1137 if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
1139 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1141 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
1142 } else if (info == &mode_info[IDX_oddp]) {
1144 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1146 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
1147 } else if (info == &mode_info[IDX_nl]) {
1149 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
1150 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
1152 mode->c_iflag = mode->c_iflag & ~ICRNL;
1153 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1155 } else if (info == &mode_info[IDX_ek]) {
1156 mode->c_cc[VERASE] = CERASE;
1157 mode->c_cc[VKILL] = CKILL;
1158 } else if (info == &mode_info[IDX_sane]) {
1160 } else if (info == &mode_info[IDX_cbreak]) {
1162 mode->c_lflag |= ICANON;
1164 mode->c_lflag &= ~ICANON;
1165 } else if (info == &mode_info[IDX_pass8]) {
1167 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1168 mode->c_iflag |= ISTRIP;
1170 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1171 mode->c_iflag &= ~ISTRIP;
1173 } else if (info == &mode_info[IDX_litout]) {
1175 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1176 mode->c_iflag |= ISTRIP;
1177 mode->c_oflag |= OPOST;
1179 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1180 mode->c_iflag &= ~ISTRIP;
1181 mode->c_oflag &= ~OPOST;
1183 } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
1184 if ((info == &mode_info[IDX_raw] && reversed)
1185 || (info == &mode_info[IDX_cooked] && !reversed)
1188 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1189 mode->c_oflag |= OPOST;
1190 mode->c_lflag |= ISIG | ICANON;
1192 mode->c_cc[VEOF] = CEOF;
1195 mode->c_cc[VEOL] = CEOL;
1200 mode->c_oflag &= ~OPOST;
1201 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1202 mode->c_cc[VMIN] = 1;
1203 mode->c_cc[VTIME] = 0;
1207 else if (info == &mode_info[IDX_decctlq]) {
1209 mode->c_iflag |= IXANY;
1211 mode->c_iflag &= ~IXANY;
1215 else if (info == &mode_info[IDX_tabs]) {
1217 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1219 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1223 else if (info == &mode_info[IDX_tabs]) {
1225 mode->c_oflag |= OXTABS;
1227 mode->c_oflag &= ~OXTABS;
1230 #if XCASE && IUCLC && OLCUC
1231 else if (info==&mode_info[IDX_lcase] || info==&mode_info[IDX_LCASE]) {
1233 mode->c_lflag &= ~XCASE;
1234 mode->c_iflag &= ~IUCLC;
1235 mode->c_oflag &= ~OLCUC;
1237 mode->c_lflag |= XCASE;
1238 mode->c_iflag |= IUCLC;
1239 mode->c_oflag |= OLCUC;
1243 else if (info == &mode_info[IDX_crt]) {
1244 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1245 } else if (info == &mode_info[IDX_dec]) {
1246 mode->c_cc[VINTR] = 3; /* ^C */
1247 mode->c_cc[VERASE] = 127; /* DEL */
1248 mode->c_cc[VKILL] = 21; /* ^U */
1249 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1250 if (IXANY) mode->c_iflag &= ~IXANY;
1254 static void set_control_char_or_die(const struct control_info *info,
1255 const char *arg, struct termios *mode)
1257 unsigned char value;
1259 if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
1260 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1261 else if (arg[0] == '\0' || arg[1] == '\0')
1263 else if (strcmp(arg, "^-") == 0 || strcmp(arg, "undef") == 0)
1264 value = _POSIX_VDISABLE;
1265 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1266 value = arg[1] & 0x1f; /* Non-letters get weird results */
1270 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1271 mode->c_cc[info->offset] = value;
1274 #define STTY_require_set_attr (1 << 0)
1275 #define STTY_speed_was_set (1 << 1)
1276 #define STTY_verbose_output (1 << 2)
1277 #define STTY_recoverable_output (1 << 3)
1278 #define STTY_noargs (1 << 4)
1280 int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1281 int stty_main(int argc UNUSED_PARAM, char **argv)
1283 struct termios mode;
1284 void (*output_func)(const struct termios *, int);
1285 const char *file_name = NULL;
1286 int display_all = 0;
1292 stty_state = STTY_noargs;
1293 output_func = do_display;
1295 /* First pass: only parse/verify command line params */
1298 const struct mode_info *mp;
1299 const struct control_info *cp;
1300 const char *arg = argv[k];
1301 const char *argnext = argv[k+1];
1304 if (arg[0] == '-') {
1306 mp = find_mode(arg+1);
1308 if (!(mp->flags & REV))
1309 goto invalid_argument;
1310 stty_state &= ~STTY_noargs;
1313 /* It is an option - parse it */
1318 stty_state |= STTY_verbose_output;
1319 output_func = do_display;
1323 stty_state |= STTY_recoverable_output;
1324 output_func = display_recoverable;
1328 bb_error_msg_and_die("only one device may be specified");
1329 file_name = &arg[i+1]; /* "-Fdevice" ? */
1330 if (!file_name[0]) { /* nope, "-F device" */
1331 int p = k+1; /* argv[p] is argnext */
1332 file_name = argnext;
1334 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1335 /* remove -F param from arg[vc] */
1337 argv[p] = argv[p+1];
1343 goto invalid_argument;
1350 mp = find_mode(arg);
1352 stty_state &= ~STTY_noargs;
1356 cp = find_control(arg);
1359 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1360 /* called for the side effect of xfunc death only */
1361 set_control_char_or_die(cp, argnext, &mode);
1362 stty_state &= ~STTY_noargs;
1367 param = find_param(arg);
1368 if (param & param_need_arg) {
1370 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1378 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1380 # endif /* else fall-through */
1386 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1393 /* called for the side effect of xfunc death only */
1394 set_speed_or_die(input_speed, argnext, &mode);
1397 /* called for the side effect of xfunc death only */
1398 set_speed_or_die(output_speed, argnext, &mode);
1401 if (recover_mode(arg, &mode) == 1) break;
1402 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1404 bb_error_msg_and_die("invalid argument '%s'", arg);
1406 stty_state &= ~STTY_noargs;
1409 /* Specifying both -a and -g is an error */
1410 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1411 (STTY_verbose_output | STTY_recoverable_output)
1413 bb_error_msg_and_die("-a and -g are mutually exclusive");
1415 /* Specifying -a or -g with non-options is an error */
1416 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output))
1417 && !(stty_state & STTY_noargs)
1419 bb_error_msg_and_die("modes may not be set when -a or -g is used");
1422 /* Now it is safe to start doing things */
1424 G.device_name = file_name;
1425 xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO);
1426 ndelay_off(STDIN_FILENO);
1429 /* Initialize to all zeroes so there is no risk memcmp will report a
1430 spurious difference in an uninitialized portion of the structure */
1431 memset(&mode, 0, sizeof(mode));
1432 if (tcgetattr(STDIN_FILENO, &mode))
1433 perror_on_device_and_die("%s");
1435 if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
1436 G.max_col = get_terminal_width(STDOUT_FILENO);
1437 output_func(&mode, display_all);
1438 return EXIT_SUCCESS;
1441 /* Second pass: perform actions */
1444 const struct mode_info *mp;
1445 const struct control_info *cp;
1446 const char *arg = argv[k];
1447 const char *argnext = argv[k+1];
1450 if (arg[0] == '-') {
1451 mp = find_mode(arg+1);
1453 set_mode(mp, 1 /* reversed */, &mode);
1454 stty_state |= STTY_require_set_attr;
1456 /* It is an option - already parsed. Skip it */
1460 mp = find_mode(arg);
1462 set_mode(mp, 0 /* non-reversed */, &mode);
1463 stty_state |= STTY_require_set_attr;
1467 cp = find_control(arg);
1470 set_control_char_or_die(cp, argnext, &mode);
1471 stty_state |= STTY_require_set_attr;
1475 param = find_param(arg);
1476 if (param & param_need_arg) {
1483 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1484 stty_state |= STTY_require_set_attr;
1490 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1493 display_window_size(0);
1496 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1500 display_speed(&mode, 0);
1503 set_speed_or_die(input_speed, argnext, &mode);
1504 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1507 set_speed_or_die(output_speed, argnext, &mode);
1508 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1511 if (recover_mode(arg, &mode) == 1)
1512 stty_state |= STTY_require_set_attr;
1513 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
1514 set_speed_or_die(both_speeds, arg, &mode);
1515 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1516 } /* else - impossible (caught in the first pass):
1517 bb_error_msg_and_die("invalid argument '%s'", arg); */
1521 if (stty_state & STTY_require_set_attr) {
1522 struct termios new_mode;
1524 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1525 perror_on_device_and_die("%s");
1527 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1528 it performs *any* of the requested operations. This means it
1529 can report 'success' when it has actually failed to perform
1530 some proper subset of the requested operations. To detect
1531 this partial failure, get the current terminal attributes and
1532 compare them to the requested ones */
1534 /* Initialize to all zeroes so there is no risk memcmp will report a
1535 spurious difference in an uninitialized portion of the structure */
1536 memset(&new_mode, 0, sizeof(new_mode));
1537 if (tcgetattr(STDIN_FILENO, &new_mode))
1538 perror_on_device_and_die("%s");
1540 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1542 * I think the below chunk is not necessary on Linux.
1543 * If you are deleting it, also delete STTY_speed_was_set bit -
1544 * it is only ever checked here.
1546 #if 0 /* was "if CIBAUD" */
1547 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1548 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1549 sometimes (m1 != m2). The only difference is in the four bits
1550 of the c_cflag field corresponding to the baud rate. To save
1551 Sun users a little confusion, don't report an error if this
1552 happens. But suppress the error only if we haven't tried to
1553 set the baud rate explicitly -- otherwise we'd never give an
1554 error for a true failure to set the baud rate */
1556 new_mode.c_cflag &= (~CIBAUD);
1557 if ((stty_state & STTY_speed_was_set)
1558 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1560 perror_on_device_and_die("%s: cannot perform all requested operations");
1564 return EXIT_SUCCESS;