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
24 //usage:#define stty_trivial_usage
25 //usage: "[-a|g] [-F DEVICE] [SETTING]..."
26 //usage:#define stty_full_usage "\n\n"
27 //usage: "Without arguments, prints baud rate, line discipline,\n"
28 //usage: "and deviations from stty sane\n"
29 //usage: "\n -F DEVICE Open device instead of stdin"
30 //usage: "\n -a Print all current settings in human-readable form"
31 //usage: "\n -g Print in stty-readable form"
32 //usage: "\n [SETTING] See manpage"
35 #include "common_bufsiz.h"
37 #ifndef _POSIX_VDISABLE
38 # define _POSIX_VDISABLE ((unsigned char) 0)
41 #define Control(c) ((c) & 0x1f)
42 /* Canonical values for control characters */
44 # define CINTR Control('c')
53 # define CKILL Control('u')
56 # define CEOF Control('d')
59 # define CEOL _POSIX_VDISABLE
62 # define CSTART Control('q')
65 # define CSTOP Control('s')
68 # define CSUSP Control('z')
70 #if defined(VEOL2) && !defined(CEOL2)
71 # define CEOL2 _POSIX_VDISABLE
73 /* glibc-2.12.1 uses only VSWTC name */
74 #if defined(VSWTC) && !defined(VSWTCH)
77 /* ISC renamed swtch to susp for termios, but we'll accept either name */
78 #if defined(VSUSP) && !defined(VSWTCH)
82 #if defined(VSWTCH) && !defined(CSWTCH)
83 # define CSWTCH _POSIX_VDISABLE
86 /* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'.
87 So the default is to disable 'swtch.' */
88 #if defined(__sparc__) && defined(__svr4__)
90 # define CSWTCH _POSIX_VDISABLE
93 #if defined(VWERSE) && !defined(VWERASE) /* AIX-3.2.5 */
94 # define VWERASE VWERSE
96 #if defined(VDSUSP) && !defined(CDSUSP)
97 # define CDSUSP Control('y')
99 #if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */
100 # define VREPRINT VRPRNT
102 #if defined(VREPRINT) && !defined(CRPRNT)
103 # define CRPRNT Control('r')
105 #if defined(VWERASE) && !defined(CWERASE)
106 # define CWERASE Control('w')
108 #if defined(VLNEXT) && !defined(CLNEXT)
109 # define CLNEXT Control('v')
111 #if defined(VDISCARD) && !defined(VFLUSHO)
112 # define VFLUSHO VDISCARD
114 #if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */
115 # define VFLUSHO VFLUSH
117 #if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */
118 # define ECHOCTL CTLECH
120 #if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */
121 # define ECHOCTL TCTLECH
123 #if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */
124 # define ECHOKE CRTKIL
126 #if defined(VFLUSHO) && !defined(CFLUSHO)
127 # define CFLUSHO Control('o')
129 #if defined(VSTATUS) && !defined(CSTATUS)
130 # define CSTATUS Control('t')
133 /* Save us from #ifdef forest plague */
243 /* Which speeds to set */
245 input_speed, output_speed, both_speeds
248 /* Which member(s) of 'struct termios' a mode uses */
250 control, input, output, local, combination
252 static tcflag_t *get_ptr_to_tcflag(unsigned type, const struct termios *mode)
254 static const uint8_t tcflag_offsets[] ALIGN1 = {
255 offsetof(struct termios, c_cflag), /* control */
256 offsetof(struct termios, c_iflag), /* input */
257 offsetof(struct termios, c_oflag), /* output */
258 offsetof(struct termios, c_lflag) /* local */
261 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
266 /* Flags for 'struct mode_info' */
267 #define SANE_SET 1 /* Set in 'sane' mode */
268 #define SANE_UNSET 2 /* Unset in 'sane' mode */
269 #define REV 4 /* Can be turned off by prepending '-' */
270 #define OMIT 8 /* Don't display value */
274 * This structure should be kept as small as humanly possible.
277 const uint8_t type; /* Which structure element to change */
278 const uint8_t flags; /* Setting and display options */
279 /* only these values are ever used, so... */
280 #if (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100
282 #elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000
285 const tcflag_t mask; /* Other bits to turn off for this mode */
287 /* was using short here, but ppc32 was unhappy */
288 const tcflag_t bits; /* Bits to set for this mode */
292 /* Must match mode_name[] and mode_info[] order! */
312 #if XCASE && IUCLC && OLCUC
318 #define MI_ENTRY(N,T,F,B,M) N "\0"
320 /* Mode names given on command line */
321 static const char mode_name[] ALIGN1 =
322 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
323 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
324 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
325 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
326 MI_ENTRY("ek", combination, OMIT, 0, 0 )
327 MI_ENTRY("sane", combination, OMIT, 0, 0 )
328 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
329 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
330 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
331 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
332 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
333 MI_ENTRY("crt", combination, OMIT, 0, 0 )
334 MI_ENTRY("dec", combination, OMIT, 0, 0 )
336 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
339 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
341 #if XCASE && IUCLC && OLCUC
342 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 )
343 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 )
345 MI_ENTRY("parenb", control, REV, PARENB, 0 )
346 MI_ENTRY("parodd", control, REV, PARODD, 0 )
347 MI_ENTRY("cs5", control, 0, CS5, CSIZE)
348 MI_ENTRY("cs6", control, 0, CS6, CSIZE)
349 MI_ENTRY("cs7", control, 0, CS7, CSIZE)
350 MI_ENTRY("cs8", control, 0, CS8, CSIZE)
351 MI_ENTRY("hupcl", control, REV, HUPCL, 0 )
352 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 )
353 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 )
354 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 )
355 MI_ENTRY("clocal", control, REV, CLOCAL, 0 )
357 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
359 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 )
360 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 )
361 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 )
362 MI_ENTRY("parmrk", input, REV, PARMRK, 0 )
363 MI_ENTRY("inpck", input, REV, INPCK, 0 )
364 MI_ENTRY("istrip", input, REV, ISTRIP, 0 )
365 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 )
366 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 )
367 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
368 MI_ENTRY("ixon", input, REV, IXON, 0 )
369 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
370 MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 )
372 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
375 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
378 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
381 MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 )
383 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
385 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
388 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
391 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
394 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
397 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
400 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
403 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
406 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
407 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
410 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY)
411 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY)
412 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY)
413 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY)
417 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY)
419 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY)
422 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY)
424 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY)
427 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
432 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
433 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
436 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
437 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
440 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
441 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
443 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
444 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
446 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
448 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
449 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
450 MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 )
451 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
452 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
453 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
455 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
458 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
461 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
462 MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 )
465 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
466 MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 )
469 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
470 MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 )
475 #define MI_ENTRY(N,T,F,B,M) { T, F, M, B },
477 static const struct mode_info mode_info[] = {
478 /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */
479 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
480 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
481 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
482 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
483 MI_ENTRY("ek", combination, OMIT, 0, 0 )
484 MI_ENTRY("sane", combination, OMIT, 0, 0 )
485 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
486 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
487 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
488 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
489 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
490 MI_ENTRY("crt", combination, OMIT, 0, 0 )
491 MI_ENTRY("dec", combination, OMIT, 0, 0 )
493 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
496 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
498 #if XCASE && IUCLC && OLCUC
499 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 )
500 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 )
502 MI_ENTRY("parenb", control, REV, PARENB, 0 )
503 MI_ENTRY("parodd", control, REV, PARODD, 0 )
504 MI_ENTRY("cs5", control, 0, CS5, CSIZE)
505 MI_ENTRY("cs6", control, 0, CS6, CSIZE)
506 MI_ENTRY("cs7", control, 0, CS7, CSIZE)
507 MI_ENTRY("cs8", control, 0, CS8, CSIZE)
508 MI_ENTRY("hupcl", control, REV, HUPCL, 0 )
509 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 )
510 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 )
511 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 )
512 MI_ENTRY("clocal", control, REV, CLOCAL, 0 )
514 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
516 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 )
517 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 )
518 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 )
519 MI_ENTRY("parmrk", input, REV, PARMRK, 0 )
520 MI_ENTRY("inpck", input, REV, INPCK, 0 )
521 MI_ENTRY("istrip", input, REV, ISTRIP, 0 )
522 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 )
523 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 )
524 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
525 MI_ENTRY("ixon", input, REV, IXON, 0 )
526 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
527 MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 )
529 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
532 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
535 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
538 MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 )
540 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
542 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
545 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
548 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
551 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
554 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
557 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
560 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
563 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
564 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
567 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY)
568 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY)
569 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY)
570 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY)
574 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY)
576 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY)
579 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY)
581 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY)
584 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
589 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
590 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
593 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
594 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
597 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
598 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
600 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
601 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
603 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
605 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
606 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
607 MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 )
608 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
609 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
610 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
612 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
615 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
618 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
619 MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 )
622 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
623 MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 )
626 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
627 MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 )
632 NUM_mode_info = ARRAY_SIZE(mode_info)
636 /* Control characters */
637 struct control_info {
638 const uint8_t saneval; /* Value to set for 'stty sane' */
639 const uint8_t offset; /* Offset in c_cc */
643 /* Must match control_name[] and control_info[] order! */
681 #define CI_ENTRY(n,s,o) n "\0"
683 /* Name given on command line */
684 static const char control_name[] ALIGN1 =
685 CI_ENTRY("intr", CINTR, VINTR )
686 CI_ENTRY("quit", CQUIT, VQUIT )
687 CI_ENTRY("erase", CERASE, VERASE )
688 CI_ENTRY("kill", CKILL, VKILL )
689 CI_ENTRY("eof", CEOF, VEOF )
690 CI_ENTRY("eol", CEOL, VEOL )
692 CI_ENTRY("eol2", CEOL2, VEOL2 )
695 CI_ENTRY("swtch", CSWTCH, VSWTCH )
697 CI_ENTRY("start", CSTART, VSTART )
698 CI_ENTRY("stop", CSTOP, VSTOP )
699 CI_ENTRY("susp", CSUSP, VSUSP )
701 CI_ENTRY("dsusp", CDSUSP, VDSUSP )
704 CI_ENTRY("rprnt", CRPRNT, VREPRINT)
707 CI_ENTRY("werase", CWERASE, VWERASE )
710 CI_ENTRY("lnext", CLNEXT, VLNEXT )
713 CI_ENTRY("flush", CFLUSHO, VFLUSHO )
716 CI_ENTRY("status", CSTATUS, VSTATUS )
718 /* These must be last because of the display routines */
719 CI_ENTRY("min", 1, VMIN )
720 CI_ENTRY("time", 0, VTIME )
724 #define CI_ENTRY(n,s,o) { s, o },
726 static const struct control_info control_info[] ALIGN2 = {
727 /* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */
728 CI_ENTRY("intr", CINTR, VINTR )
729 CI_ENTRY("quit", CQUIT, VQUIT )
730 CI_ENTRY("erase", CERASE, VERASE )
731 CI_ENTRY("kill", CKILL, VKILL )
732 CI_ENTRY("eof", CEOF, VEOF )
733 CI_ENTRY("eol", CEOL, VEOL )
735 CI_ENTRY("eol2", CEOL2, VEOL2 )
738 CI_ENTRY("swtch", CSWTCH, VSWTCH )
740 CI_ENTRY("start", CSTART, VSTART )
741 CI_ENTRY("stop", CSTOP, VSTOP )
742 CI_ENTRY("susp", CSUSP, VSUSP )
744 CI_ENTRY("dsusp", CDSUSP, VDSUSP )
747 CI_ENTRY("rprnt", CRPRNT, VREPRINT)
750 CI_ENTRY("werase", CWERASE, VWERASE )
753 CI_ENTRY("lnext", CLNEXT, VLNEXT )
756 CI_ENTRY("flush", CFLUSHO, VFLUSHO )
759 CI_ENTRY("status", CSTATUS, VSTATUS )
761 /* These must be last because of the display routines */
762 CI_ENTRY("min", 1, VMIN )
763 CI_ENTRY("time", 0, VTIME )
767 NUM_control_info = ARRAY_SIZE(control_info)
772 const char *device_name;
773 /* The width of the screen, for output wrapping */
775 /* Current position, to know when to wrap */
776 unsigned current_col;
779 #define G (*(struct globals*)bb_common_bufsiz1)
780 #define INIT_G() do { \
781 G.device_name = bb_msg_standard_input; \
785 static void set_speed_or_die(enum speed_setting type, const char *arg,
786 struct termios *mode)
790 baud = tty_value_to_baud(xatou(arg));
792 if (type != output_speed) { /* either input or both */
793 cfsetispeed(mode, baud);
795 if (type != input_speed) { /* either output or both */
796 cfsetospeed(mode, baud);
800 static NORETURN void perror_on_device_and_die(const char *fmt)
802 bb_perror_msg_and_die(fmt, G.device_name);
805 static void perror_on_device(const char *fmt)
807 bb_perror_msg(fmt, G.device_name);
810 /* Print format string MESSAGE and optional args.
811 Wrap to next line first if it won't fit.
812 Print a space first unless MESSAGE will start a new line */
813 static void wrapf(const char *message, ...)
819 va_start(args, message);
820 buflen = vsnprintf(buf, sizeof(buf), message, args);
822 /* We seem to be called only with suitable lengths, but check if
823 somebody failed to adhere to this assumption just to be sure. */
824 if (!buflen || buflen >= sizeof(buf)) return;
826 if (G.current_col > 0) {
828 if (buf[0] != '\n') {
829 if (G.current_col + buflen >= G.max_col) {
837 G.current_col += buflen;
838 if (buf[buflen-1] == '\n')
842 static void newline(void)
844 if (G.current_col != 0)
849 static void set_window_size(int rows, int cols)
851 struct winsize win = { 0, 0, 0, 0 };
853 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
854 if (errno != EINVAL) {
857 memset(&win, 0, sizeof(win));
865 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
867 perror_on_device("%s");
871 static void display_window_size(int fancy)
873 const char *fmt_str = "%s\0%s: no size information for this device";
874 unsigned width, height;
876 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
877 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
878 perror_on_device(fmt_str);
881 wrapf(fancy ? "rows %u; columns %u;" : "%u %u\n",
886 static const struct suffix_mult stty_suffixes[] = {
893 static const struct mode_info *find_mode(const char *name)
895 int i = index_in_strings(mode_name, name);
896 return i >= 0 ? &mode_info[i] : NULL;
899 static const struct control_info *find_control(const char *name)
901 int i = index_in_strings(control_name, name);
902 return i >= 0 ? &control_info[i] : NULL;
906 param_need_arg = 0x80,
907 param_line = 1 | 0x80,
908 param_rows = 2 | 0x80,
909 param_cols = 3 | 0x80,
910 param_columns = 4 | 0x80,
913 param_ispeed = 7 | 0x80,
914 param_ospeed = 8 | 0x80,
917 static int find_param(const char *name)
919 static const char params[] ALIGN1 =
928 int i = index_in_strings(params, name) + 1;
931 if (i != 5 && i != 6)
936 static int recover_mode(const char *arg, struct termios *mode)
940 unsigned long iflag, oflag, cflag, lflag;
942 /* Scan into temporaries since it is too much trouble to figure out
943 the right format for 'tcflag_t' */
944 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
945 &iflag, &oflag, &cflag, &lflag, &n) != 4)
947 mode->c_iflag = iflag;
948 mode->c_oflag = oflag;
949 mode->c_cflag = cflag;
950 mode->c_lflag = lflag;
952 for (i = 0; i < NCCS; ++i) {
953 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
959 /* Fail if there are too many fields */
966 static void display_recoverable(const struct termios *mode,
967 int UNUSED_PARAM dummy)
970 printf("%lx:%lx:%lx:%lx",
971 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
972 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
973 for (i = 0; i < NCCS; ++i)
974 printf(":%x", (unsigned int) mode->c_cc[i]);
978 static void display_speed(const struct termios *mode, int fancy)
980 //____________________ 01234567 8 9
981 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
982 unsigned long ispeed, ospeed;
984 ispeed = cfgetispeed(mode);
985 ospeed = cfgetospeed(mode);
986 if (ispeed == 0 || ispeed == ospeed) {
987 ispeed = ospeed; /* in case ispeed was 0 */
988 //________ 0123 4 5 6 7 8 9
989 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
991 if (fancy) fmt_str += 9;
992 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
995 static void do_display(const struct termios *mode, int all)
1000 int prev_type = control;
1002 display_speed(mode, 1);
1004 display_window_size(1);
1006 wrapf("line = %u;\n", mode->c_line);
1011 for (i = 0; i != CIDX_min; ++i) {
1013 /* If swtch is the same as susp, don't print both */
1015 if (i == CIDX_swtch)
1018 /* If eof uses the same slot as min, only print whichever applies */
1020 if (!(mode->c_lflag & ICANON)
1021 && (i == CIDX_eof || i == CIDX_eol)
1026 ch = mode->c_cc[control_info[i].offset];
1027 if (ch == _POSIX_VDISABLE)
1028 strcpy(G.buf, "<undef>");
1030 visible(ch, G.buf, 0);
1031 wrapf("%s = %s;", nth_string(control_name, i), G.buf);
1034 if ((mode->c_lflag & ICANON) == 0)
1036 wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1039 for (i = 0; i < NUM_mode_info; ++i) {
1040 if (mode_info[i].flags & OMIT)
1042 if (mode_info[i].type != prev_type) {
1044 prev_type = mode_info[i].type;
1047 bitsp = get_ptr_to_tcflag(mode_info[i].type, mode);
1048 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1049 if ((*bitsp & mask) == mode_info[i].bits) {
1050 if (all || (mode_info[i].flags & SANE_UNSET))
1051 wrapf("-%s"+1, nth_string(mode_name, i));
1053 if ((all && mode_info[i].flags & REV)
1054 || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
1056 wrapf("-%s", nth_string(mode_name, i));
1063 static void sane_mode(struct termios *mode)
1067 for (i = 0; i < NUM_control_info; ++i) {
1072 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1075 for (i = 0; i < NUM_mode_info; ++i) {
1077 tcflag_t *bitsp = get_ptr_to_tcflag(mode_info[i].type, mode);
1081 val = *bitsp & ~((unsigned long)mode_info[i].mask);
1082 if (mode_info[i].flags & SANE_SET) {
1083 *bitsp = val | mode_info[i].bits;
1085 if (mode_info[i].flags & SANE_UNSET) {
1086 *bitsp = val & ~mode_info[i].bits;
1091 static void set_mode(const struct mode_info *info, int reversed,
1092 struct termios *mode)
1096 bitsp = get_ptr_to_tcflag(info->type, mode);
1099 tcflag_t val = *bitsp & ~info->mask;
1101 *bitsp = val & ~info->bits;
1103 *bitsp = val | info->bits;
1107 /* !bitsp - it's a "combination" mode */
1108 if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
1110 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1112 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
1113 } else if (info == &mode_info[IDX_oddp]) {
1115 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1117 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
1118 } else if (info == &mode_info[IDX_nl]) {
1120 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
1121 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
1123 mode->c_iflag = mode->c_iflag & ~ICRNL;
1124 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1126 } else if (info == &mode_info[IDX_ek]) {
1127 mode->c_cc[VERASE] = CERASE;
1128 mode->c_cc[VKILL] = CKILL;
1129 } else if (info == &mode_info[IDX_sane]) {
1131 } else if (info == &mode_info[IDX_cbreak]) {
1133 mode->c_lflag |= ICANON;
1135 mode->c_lflag &= ~ICANON;
1136 } else if (info == &mode_info[IDX_pass8]) {
1138 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1139 mode->c_iflag |= ISTRIP;
1141 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1142 mode->c_iflag &= ~ISTRIP;
1144 } else if (info == &mode_info[IDX_litout]) {
1146 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1147 mode->c_iflag |= ISTRIP;
1148 mode->c_oflag |= OPOST;
1150 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1151 mode->c_iflag &= ~ISTRIP;
1152 mode->c_oflag &= ~OPOST;
1154 } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
1155 if ((info == &mode_info[IDX_raw] && reversed)
1156 || (info == &mode_info[IDX_cooked] && !reversed)
1159 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1160 mode->c_oflag |= OPOST;
1161 mode->c_lflag |= ISIG | ICANON;
1163 mode->c_cc[VEOF] = CEOF;
1166 mode->c_cc[VEOL] = CEOL;
1171 mode->c_oflag &= ~OPOST;
1172 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1173 mode->c_cc[VMIN] = 1;
1174 mode->c_cc[VTIME] = 0;
1178 else if (info == &mode_info[IDX_decctlq]) {
1180 mode->c_iflag |= IXANY;
1182 mode->c_iflag &= ~IXANY;
1186 else if (info == &mode_info[IDX_tabs]) {
1188 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1190 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1194 else if (info == &mode_info[IDX_tabs]) {
1196 mode->c_oflag |= OXTABS;
1198 mode->c_oflag &= ~OXTABS;
1201 #if XCASE && IUCLC && OLCUC
1202 else if (info==&mode_info[IDX_lcase] || info==&mode_info[IDX_LCASE]) {
1204 mode->c_lflag &= ~XCASE;
1205 mode->c_iflag &= ~IUCLC;
1206 mode->c_oflag &= ~OLCUC;
1208 mode->c_lflag |= XCASE;
1209 mode->c_iflag |= IUCLC;
1210 mode->c_oflag |= OLCUC;
1214 else if (info == &mode_info[IDX_crt]) {
1215 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1216 } else if (info == &mode_info[IDX_dec]) {
1217 mode->c_cc[VINTR] = 3; /* ^C */
1218 mode->c_cc[VERASE] = 127; /* DEL */
1219 mode->c_cc[VKILL] = 21; /* ^U */
1220 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1221 if (IXANY) mode->c_iflag &= ~IXANY;
1225 static void set_control_char_or_die(const struct control_info *info,
1226 const char *arg, struct termios *mode)
1228 unsigned char value;
1230 if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
1231 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1232 else if (arg[0] == '\0' || arg[1] == '\0')
1234 else if (strcmp(arg, "^-") == 0 || strcmp(arg, "undef") == 0)
1235 value = _POSIX_VDISABLE;
1236 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1237 value = arg[1] & 0x1f; /* Non-letters get weird results */
1241 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1242 mode->c_cc[info->offset] = value;
1245 #define STTY_require_set_attr (1 << 0)
1246 #define STTY_speed_was_set (1 << 1)
1247 #define STTY_verbose_output (1 << 2)
1248 #define STTY_recoverable_output (1 << 3)
1249 #define STTY_noargs (1 << 4)
1251 int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1252 int stty_main(int argc UNUSED_PARAM, char **argv)
1254 struct termios mode;
1255 void (*output_func)(const struct termios *, int);
1256 const char *file_name = NULL;
1257 int display_all = 0;
1263 stty_state = STTY_noargs;
1264 output_func = do_display;
1266 /* First pass: only parse/verify command line params */
1269 const struct mode_info *mp;
1270 const struct control_info *cp;
1271 const char *arg = argv[k];
1272 const char *argnext = argv[k+1];
1275 if (arg[0] == '-') {
1277 mp = find_mode(arg+1);
1279 if (!(mp->flags & REV))
1280 goto invalid_argument;
1281 stty_state &= ~STTY_noargs;
1284 /* It is an option - parse it */
1289 stty_state |= STTY_verbose_output;
1290 output_func = do_display;
1294 stty_state |= STTY_recoverable_output;
1295 output_func = display_recoverable;
1299 bb_error_msg_and_die("only one device may be specified");
1300 file_name = &arg[i+1]; /* "-Fdevice" ? */
1301 if (!file_name[0]) { /* nope, "-F device" */
1302 int p = k+1; /* argv[p] is argnext */
1303 file_name = argnext;
1305 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1306 /* remove -F param from arg[vc] */
1308 argv[p] = argv[p+1];
1314 goto invalid_argument;
1321 mp = find_mode(arg);
1323 stty_state &= ~STTY_noargs;
1327 cp = find_control(arg);
1330 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1331 /* called for the side effect of xfunc death only */
1332 set_control_char_or_die(cp, argnext, &mode);
1333 stty_state &= ~STTY_noargs;
1338 param = find_param(arg);
1339 if (param & param_need_arg) {
1341 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1349 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1351 # endif /* else fall-through */
1357 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1364 /* called for the side effect of xfunc death only */
1365 set_speed_or_die(input_speed, argnext, &mode);
1368 /* called for the side effect of xfunc death only */
1369 set_speed_or_die(output_speed, argnext, &mode);
1372 if (recover_mode(arg, &mode) == 1) break;
1373 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1375 bb_error_msg_and_die("invalid argument '%s'", arg);
1377 stty_state &= ~STTY_noargs;
1380 /* Specifying both -a and -g is an error */
1381 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1382 (STTY_verbose_output | STTY_recoverable_output)
1384 bb_error_msg_and_die("-a and -g are mutually exclusive");
1386 /* Specifying -a or -g with non-options is an error */
1387 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output))
1388 && !(stty_state & STTY_noargs)
1390 bb_error_msg_and_die("modes may not be set when -a or -g is used");
1393 /* Now it is safe to start doing things */
1395 G.device_name = file_name;
1396 xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO);
1397 ndelay_off(STDIN_FILENO);
1400 /* Initialize to all zeroes so there is no risk memcmp will report a
1401 spurious difference in an uninitialized portion of the structure */
1402 memset(&mode, 0, sizeof(mode));
1403 if (tcgetattr(STDIN_FILENO, &mode))
1404 perror_on_device_and_die("%s");
1406 if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
1407 G.max_col = get_terminal_width(STDOUT_FILENO);
1408 output_func(&mode, display_all);
1409 return EXIT_SUCCESS;
1412 /* Second pass: perform actions */
1415 const struct mode_info *mp;
1416 const struct control_info *cp;
1417 const char *arg = argv[k];
1418 const char *argnext = argv[k+1];
1421 if (arg[0] == '-') {
1422 mp = find_mode(arg+1);
1424 set_mode(mp, 1 /* reversed */, &mode);
1425 stty_state |= STTY_require_set_attr;
1427 /* It is an option - already parsed. Skip it */
1431 mp = find_mode(arg);
1433 set_mode(mp, 0 /* non-reversed */, &mode);
1434 stty_state |= STTY_require_set_attr;
1438 cp = find_control(arg);
1441 set_control_char_or_die(cp, argnext, &mode);
1442 stty_state |= STTY_require_set_attr;
1446 param = find_param(arg);
1447 if (param & param_need_arg) {
1454 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1455 stty_state |= STTY_require_set_attr;
1461 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1464 display_window_size(0);
1467 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1471 display_speed(&mode, 0);
1474 set_speed_or_die(input_speed, argnext, &mode);
1475 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1478 set_speed_or_die(output_speed, argnext, &mode);
1479 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1482 if (recover_mode(arg, &mode) == 1)
1483 stty_state |= STTY_require_set_attr;
1484 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
1485 set_speed_or_die(both_speeds, arg, &mode);
1486 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1487 } /* else - impossible (caught in the first pass):
1488 bb_error_msg_and_die("invalid argument '%s'", arg); */
1492 if (stty_state & STTY_require_set_attr) {
1493 struct termios new_mode;
1495 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1496 perror_on_device_and_die("%s");
1498 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1499 it performs *any* of the requested operations. This means it
1500 can report 'success' when it has actually failed to perform
1501 some proper subset of the requested operations. To detect
1502 this partial failure, get the current terminal attributes and
1503 compare them to the requested ones */
1505 /* Initialize to all zeroes so there is no risk memcmp will report a
1506 spurious difference in an uninitialized portion of the structure */
1507 memset(&new_mode, 0, sizeof(new_mode));
1508 if (tcgetattr(STDIN_FILENO, &new_mode))
1509 perror_on_device_and_die("%s");
1511 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1513 * I think the below chunk is not necessary on Linux.
1514 * If you are deleting it, also delete STTY_speed_was_set bit -
1515 * it is only ever checked here.
1517 #if 0 /* was "if CIBAUD" */
1518 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1519 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1520 sometimes (m1 != m2). The only difference is in the four bits
1521 of the c_cflag field corresponding to the baud rate. To save
1522 Sun users a little confusion, don't report an error if this
1523 happens. But suppress the error only if we haven't tried to
1524 set the baud rate explicitly -- otherwise we'd never give an
1525 error for a true failure to set the baud rate */
1527 new_mode.c_cflag &= (~CIBAUD);
1528 if ((stty_state & STTY_speed_was_set)
1529 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1531 perror_on_device_and_die("%s: cannot perform all requested operations");
1535 return EXIT_SUCCESS;