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
26 #ifndef _POSIX_VDISABLE
27 # define _POSIX_VDISABLE ((unsigned char) 0)
30 #define Control(c) ((c) & 0x1f)
31 /* Canonical values for control characters */
33 # define CINTR Control('c')
42 # define CKILL Control('u')
45 # define CEOF Control('d')
48 # define CEOL _POSIX_VDISABLE
51 # define CSTART Control('q')
54 # define CSTOP Control('s')
57 # define CSUSP Control('z')
59 #if defined(VEOL2) && !defined(CEOL2)
60 # define CEOL2 _POSIX_VDISABLE
62 /* ISC renamed swtch to susp for termios, but we'll accept either name */
63 #if defined(VSUSP) && !defined(VSWTCH)
67 #if defined(VSWTCH) && !defined(CSWTCH)
68 # define CSWTCH _POSIX_VDISABLE
71 /* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'.
72 So the default is to disable 'swtch.' */
73 #if defined(__sparc__) && defined(__svr4__)
75 # define CSWTCH _POSIX_VDISABLE
78 #if defined(VWERSE) && !defined(VWERASE) /* AIX-3.2.5 */
79 # define VWERASE VWERSE
81 #if defined(VDSUSP) && !defined(CDSUSP)
82 # define CDSUSP Control('y')
84 #if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */
85 # define VREPRINT VRPRNT
87 #if defined(VREPRINT) && !defined(CRPRNT)
88 # define CRPRNT Control('r')
90 #if defined(VWERASE) && !defined(CWERASE)
91 # define CWERASE Control('w')
93 #if defined(VLNEXT) && !defined(CLNEXT)
94 # define CLNEXT Control('v')
96 #if defined(VDISCARD) && !defined(VFLUSHO)
97 # define VFLUSHO VDISCARD
99 #if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */
100 # define VFLUSHO VFLUSH
102 #if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */
103 # define ECHOCTL CTLECH
105 #if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */
106 # define ECHOCTL TCTLECH
108 #if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */
109 # define ECHOKE CRTKIL
111 #if defined(VFLUSHO) && !defined(CFLUSHO)
112 # define CFLUSHO Control('o')
114 #if defined(VSTATUS) && !defined(CSTATUS)
115 # define CSTATUS Control('t')
118 /* Save us from #ifdef forest plague */
225 /* Which speeds to set */
227 input_speed, output_speed, both_speeds
230 /* Which member(s) of 'struct termios' a mode uses */
232 /* Do NOT change the order or values, as mode_type_flag()
234 control, input, output, local, combination
237 /* Flags for 'struct mode_info' */
238 #define SANE_SET 1 /* Set in 'sane' mode */
239 #define SANE_UNSET 2 /* Unset in 'sane' mode */
240 #define REV 4 /* Can be turned off by prepending '-' */
241 #define OMIT 8 /* Don't display value */
245 * This structure should be kept as small as humanly possible.
248 const uint8_t type; /* Which structure element to change */
249 const uint8_t flags; /* Setting and display options */
250 /* only these values are ever used, so... */
251 #if (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100
253 #elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000
256 const tcflag_t mask; /* Other bits to turn off for this mode */
258 /* was using short here, but ppc32 was unhappy */
259 const tcflag_t bits; /* Bits to set for this mode */
263 /* Must match mode_name[] and mode_info[] order! */
283 #if XCASE && IUCLC && OLCUC
289 #define MI_ENTRY(N,T,F,B,M) N "\0"
291 /* Mode names given on command line */
292 static const char mode_name[] =
293 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
294 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
295 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
296 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
297 MI_ENTRY("ek", combination, OMIT, 0, 0 )
298 MI_ENTRY("sane", combination, OMIT, 0, 0 )
299 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
300 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
301 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
302 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
303 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
304 MI_ENTRY("crt", combination, OMIT, 0, 0 )
305 MI_ENTRY("dec", combination, OMIT, 0, 0 )
307 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
310 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
312 #if XCASE && IUCLC && OLCUC
313 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 )
314 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 )
316 MI_ENTRY("parenb", control, REV, PARENB, 0 )
317 MI_ENTRY("parodd", control, REV, PARODD, 0 )
318 MI_ENTRY("cs5", control, 0, CS5, CSIZE)
319 MI_ENTRY("cs6", control, 0, CS6, CSIZE)
320 MI_ENTRY("cs7", control, 0, CS7, CSIZE)
321 MI_ENTRY("cs8", control, 0, CS8, CSIZE)
322 MI_ENTRY("hupcl", control, REV, HUPCL, 0 )
323 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 )
324 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 )
325 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 )
326 MI_ENTRY("clocal", control, REV, CLOCAL, 0 )
328 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
330 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 )
331 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 )
332 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 )
333 MI_ENTRY("parmrk", input, REV, PARMRK, 0 )
334 MI_ENTRY("inpck", input, REV, INPCK, 0 )
335 MI_ENTRY("istrip", input, REV, ISTRIP, 0 )
336 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 )
337 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 )
338 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
339 MI_ENTRY("ixon", input, REV, IXON, 0 )
340 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
341 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 )
343 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
346 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
349 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
351 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
353 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
356 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
359 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
362 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
365 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
368 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
371 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
374 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
375 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
378 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY)
379 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY)
380 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY)
381 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY)
385 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY)
387 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY)
390 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY)
392 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY)
395 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
400 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
401 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
404 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
405 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
408 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
409 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
411 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
412 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
414 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
416 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
417 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
418 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 )
419 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
420 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
421 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
423 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
426 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
429 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
430 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 )
433 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
434 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 )
437 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
438 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 )
443 #define MI_ENTRY(N,T,F,B,M) { T, F, M, B },
445 static const struct mode_info mode_info[] = {
446 /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */
447 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
448 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
449 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
450 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
451 MI_ENTRY("ek", combination, OMIT, 0, 0 )
452 MI_ENTRY("sane", combination, OMIT, 0, 0 )
453 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
454 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
455 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
456 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
457 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
458 MI_ENTRY("crt", combination, OMIT, 0, 0 )
459 MI_ENTRY("dec", combination, OMIT, 0, 0 )
461 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
464 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
466 #if XCASE && IUCLC && OLCUC
467 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 )
468 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 )
470 MI_ENTRY("parenb", control, REV, PARENB, 0 )
471 MI_ENTRY("parodd", control, REV, PARODD, 0 )
472 MI_ENTRY("cs5", control, 0, CS5, CSIZE)
473 MI_ENTRY("cs6", control, 0, CS6, CSIZE)
474 MI_ENTRY("cs7", control, 0, CS7, CSIZE)
475 MI_ENTRY("cs8", control, 0, CS8, CSIZE)
476 MI_ENTRY("hupcl", control, REV, HUPCL, 0 )
477 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 )
478 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 )
479 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 )
480 MI_ENTRY("clocal", control, REV, CLOCAL, 0 )
482 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
484 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 )
485 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 )
486 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 )
487 MI_ENTRY("parmrk", input, REV, PARMRK, 0 )
488 MI_ENTRY("inpck", input, REV, INPCK, 0 )
489 MI_ENTRY("istrip", input, REV, ISTRIP, 0 )
490 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 )
491 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 )
492 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
493 MI_ENTRY("ixon", input, REV, IXON, 0 )
494 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
495 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 )
497 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
500 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
503 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
505 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
507 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
510 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
513 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
516 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
519 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
522 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
525 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
528 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
529 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
532 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY)
533 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY)
534 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY)
535 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY)
539 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY)
541 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY)
544 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY)
546 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY)
549 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
554 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
555 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
558 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
559 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
562 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
563 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
565 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
566 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
568 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
570 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
571 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
572 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 )
573 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
574 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
575 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
577 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
580 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
583 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
584 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 )
587 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
588 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 )
591 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
592 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 )
597 NUM_mode_info = ARRAY_SIZE(mode_info)
601 /* Control characters */
602 struct control_info {
603 const uint8_t saneval; /* Value to set for 'stty sane' */
604 const uint8_t offset; /* Offset in c_cc */
608 /* Must match control_name[] and control_info[] order! */
646 #define CI_ENTRY(n,s,o) n "\0"
648 /* Name given on command line */
649 static const char control_name[] =
650 CI_ENTRY("intr", CINTR, VINTR )
651 CI_ENTRY("quit", CQUIT, VQUIT )
652 CI_ENTRY("erase", CERASE, VERASE )
653 CI_ENTRY("kill", CKILL, VKILL )
654 CI_ENTRY("eof", CEOF, VEOF )
655 CI_ENTRY("eol", CEOL, VEOL )
657 CI_ENTRY("eol2", CEOL2, VEOL2 )
660 CI_ENTRY("swtch", CSWTCH, VSWTCH )
662 CI_ENTRY("start", CSTART, VSTART )
663 CI_ENTRY("stop", CSTOP, VSTOP )
664 CI_ENTRY("susp", CSUSP, VSUSP )
666 CI_ENTRY("dsusp", CDSUSP, VDSUSP )
669 CI_ENTRY("rprnt", CRPRNT, VREPRINT)
672 CI_ENTRY("werase", CWERASE, VWERASE )
675 CI_ENTRY("lnext", CLNEXT, VLNEXT )
678 CI_ENTRY("flush", CFLUSHO, VFLUSHO )
681 CI_ENTRY("status", CSTATUS, VSTATUS )
683 /* These must be last because of the display routines */
684 CI_ENTRY("min", 1, VMIN )
685 CI_ENTRY("time", 0, VTIME )
689 #define CI_ENTRY(n,s,o) { s, o },
691 static const struct control_info control_info[] = {
692 /* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */
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 NUM_control_info = ARRAY_SIZE(control_info)
737 const char *device_name;
738 /* The width of the screen, for output wrapping */
740 /* Current position, to know when to wrap */
741 unsigned current_col;
744 #define G (*(struct globals*)&bb_common_bufsiz1)
745 #define INIT_G() do { \
746 G.device_name = bb_msg_standard_input; \
751 /* Return a string that is the printable representation of character CH */
752 /* Adapted from 'cat' by Torbjorn Granlund */
753 static const char *visible(unsigned ch)
757 if (ch == _POSIX_VDISABLE)
769 } else if (ch < 127) {
780 static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
782 static const uint8_t tcflag_offsets[] ALIGN1 = {
783 offsetof(struct termios, c_cflag), /* control */
784 offsetof(struct termios, c_iflag), /* input */
785 offsetof(struct termios, c_oflag), /* output */
786 offsetof(struct termios, c_lflag) /* local */
790 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
795 static void set_speed_or_die(enum speed_setting type, const char *arg,
796 struct termios *mode)
800 baud = tty_value_to_baud(xatou(arg));
802 if (type != output_speed) { /* either input or both */
803 cfsetispeed(mode, baud);
805 if (type != input_speed) { /* either output or both */
806 cfsetospeed(mode, baud);
810 static NORETURN void perror_on_device_and_die(const char *fmt)
812 bb_perror_msg_and_die(fmt, G.device_name);
815 static void perror_on_device(const char *fmt)
817 bb_perror_msg(fmt, G.device_name);
820 /* Print format string MESSAGE and optional args.
821 Wrap to next line first if it won't fit.
822 Print a space first unless MESSAGE will start a new line */
823 static void wrapf(const char *message, ...)
829 va_start(args, message);
830 buflen = vsnprintf(buf, sizeof(buf), message, args);
832 /* We seem to be called only with suitable lengths, but check if
833 somebody failed to adhere to this assumption just to be sure. */
834 if (!buflen || buflen >= sizeof(buf)) return;
836 if (G.current_col > 0) {
838 if (buf[0] != '\n') {
839 if (G.current_col + buflen >= G.max_col) {
847 G.current_col += buflen;
848 if (buf[buflen-1] == '\n')
852 static void newline(void)
854 if (G.current_col != 0)
859 static void set_window_size(int rows, int cols)
861 struct winsize win = { 0, 0, 0, 0 };
863 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
864 if (errno != EINVAL) {
867 memset(&win, 0, sizeof(win));
875 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
877 perror_on_device("%s");
881 static void display_window_size(int fancy)
883 const char *fmt_str = "%s\0%s: no size information for this device";
884 unsigned width, height;
886 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
887 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
888 perror_on_device(fmt_str);
891 wrapf(fancy ? "rows %u; columns %u;" : "%u %u\n",
896 static const struct suffix_mult stty_suffixes[] = {
903 static const struct mode_info *find_mode(const char *name)
905 int i = index_in_strings(mode_name, name);
906 return i >= 0 ? &mode_info[i] : NULL;
909 static const struct control_info *find_control(const char *name)
911 int i = index_in_strings(control_name, name);
912 return i >= 0 ? &control_info[i] : NULL;
916 param_need_arg = 0x80,
917 param_line = 1 | 0x80,
918 param_rows = 2 | 0x80,
919 param_cols = 3 | 0x80,
920 param_columns = 4 | 0x80,
923 param_ispeed = 7 | 0x80,
924 param_ospeed = 8 | 0x80,
927 static int find_param(const char *name)
929 static const char params[] ALIGN1 =
938 int i = index_in_strings(params, name) + 1;
941 if (i != 5 && i != 6)
946 static int recover_mode(const char *arg, struct termios *mode)
950 unsigned long iflag, oflag, cflag, lflag;
952 /* Scan into temporaries since it is too much trouble to figure out
953 the right format for 'tcflag_t' */
954 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
955 &iflag, &oflag, &cflag, &lflag, &n) != 4)
957 mode->c_iflag = iflag;
958 mode->c_oflag = oflag;
959 mode->c_cflag = cflag;
960 mode->c_lflag = lflag;
962 for (i = 0; i < NCCS; ++i) {
963 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
969 /* Fail if there are too many fields */
976 static void display_recoverable(const struct termios *mode,
977 int UNUSED_PARAM dummy)
980 printf("%lx:%lx:%lx:%lx",
981 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
982 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
983 for (i = 0; i < NCCS; ++i)
984 printf(":%x", (unsigned int) mode->c_cc[i]);
988 static void display_speed(const struct termios *mode, int fancy)
990 //____________________ 01234567 8 9
991 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
992 unsigned long ispeed, ospeed;
994 ospeed = ispeed = cfgetispeed(mode);
995 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
996 ispeed = ospeed; /* in case ispeed was 0 */
997 //________ 0123 4 5 6 7 8 9
998 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
1000 if (fancy) fmt_str += 9;
1001 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
1004 static void do_display(const struct termios *mode, int all)
1009 int prev_type = control;
1011 display_speed(mode, 1);
1013 display_window_size(1);
1015 wrapf("line = %u;\n", mode->c_line);
1020 for (i = 0; i != CIDX_min; ++i) {
1021 /* If swtch is the same as susp, don't print both */
1023 if (i == CIDX_swtch)
1026 /* If eof uses the same slot as min, only print whichever applies */
1028 if (!(mode->c_lflag & ICANON)
1029 && (i == CIDX_eof || i == CIDX_eol)
1034 wrapf("%s = %s;", nth_string(control_name, i),
1035 visible(mode->c_cc[control_info[i].offset]));
1038 if ((mode->c_lflag & ICANON) == 0)
1040 wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1043 for (i = 0; i < NUM_mode_info; ++i) {
1044 if (mode_info[i].flags & OMIT)
1046 if (mode_info[i].type != prev_type) {
1048 prev_type = mode_info[i].type;
1051 bitsp = mode_type_flag(mode_info[i].type, mode);
1052 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1053 if ((*bitsp & mask) == mode_info[i].bits) {
1054 if (all || (mode_info[i].flags & SANE_UNSET))
1055 wrapf("-%s"+1, nth_string(mode_name, i));
1057 if ((all && mode_info[i].flags & REV)
1058 || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
1060 wrapf("-%s", nth_string(mode_name, i));
1067 static void sane_mode(struct termios *mode)
1072 for (i = 0; i < NUM_control_info; ++i) {
1077 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1080 for (i = 0; i < NUM_mode_info; ++i) {
1081 if (mode_info[i].flags & SANE_SET) {
1082 bitsp = mode_type_flag(mode_info[i].type, mode);
1083 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
1084 | mode_info[i].bits;
1085 } else if (mode_info[i].flags & SANE_UNSET) {
1086 bitsp = mode_type_flag(mode_info[i].type, mode);
1087 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
1088 & ~mode_info[i].bits;
1093 static void set_mode(const struct mode_info *info, int reversed,
1094 struct termios *mode)
1098 bitsp = mode_type_flag(info->type, mode);
1102 *bitsp = *bitsp & ~info->mask & ~info->bits;
1104 *bitsp = (*bitsp & ~info->mask) | info->bits;
1108 /* Combination mode */
1109 if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
1111 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1113 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
1114 } else if (info == &mode_info[IDX_oddp]) {
1116 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1118 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
1119 } else if (info == &mode_info[IDX_nl]) {
1121 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
1122 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
1124 mode->c_iflag = mode->c_iflag & ~ICRNL;
1125 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1127 } else if (info == &mode_info[IDX_ek]) {
1128 mode->c_cc[VERASE] = CERASE;
1129 mode->c_cc[VKILL] = CKILL;
1130 } else if (info == &mode_info[IDX_sane]) {
1132 } else if (info == &mode_info[IDX_cbreak]) {
1134 mode->c_lflag |= ICANON;
1136 mode->c_lflag &= ~ICANON;
1137 } else if (info == &mode_info[IDX_pass8]) {
1139 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1140 mode->c_iflag |= ISTRIP;
1142 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1143 mode->c_iflag &= ~ISTRIP;
1145 } else if (info == &mode_info[IDX_litout]) {
1147 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1148 mode->c_iflag |= ISTRIP;
1149 mode->c_oflag |= OPOST;
1151 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1152 mode->c_iflag &= ~ISTRIP;
1153 mode->c_oflag &= ~OPOST;
1155 } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
1156 if ((info == &mode_info[IDX_raw] && reversed)
1157 || (info == &mode_info[IDX_cooked] && !reversed)
1160 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1161 mode->c_oflag |= OPOST;
1162 mode->c_lflag |= ISIG | ICANON;
1164 mode->c_cc[VEOF] = CEOF;
1167 mode->c_cc[VEOL] = CEOL;
1172 mode->c_oflag &= ~OPOST;
1173 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1174 mode->c_cc[VMIN] = 1;
1175 mode->c_cc[VTIME] = 0;
1179 else if (info == &mode_info[IDX_decctlq]) {
1181 mode->c_iflag |= IXANY;
1183 mode->c_iflag &= ~IXANY;
1187 else if (info == &mode_info[IDX_tabs]) {
1189 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1191 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1195 else if (info == &mode_info[IDX_tabs]) {
1197 mode->c_oflag |= OXTABS;
1199 mode->c_oflag &= ~OXTABS;
1202 #if XCASE && IUCLC && OLCUC
1203 else if (info==&mode_info[IDX_lcase] || info==&mode_info[IDX_LCASE]) {
1205 mode->c_lflag &= ~XCASE;
1206 mode->c_iflag &= ~IUCLC;
1207 mode->c_oflag &= ~OLCUC;
1209 mode->c_lflag |= XCASE;
1210 mode->c_iflag |= IUCLC;
1211 mode->c_oflag |= OLCUC;
1215 else if (info == &mode_info[IDX_crt]) {
1216 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1217 } else if (info == &mode_info[IDX_dec]) {
1218 mode->c_cc[VINTR] = 3; /* ^C */
1219 mode->c_cc[VERASE] = 127; /* DEL */
1220 mode->c_cc[VKILL] = 21; /* ^U */
1221 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1222 if (IXANY) mode->c_iflag &= ~IXANY;
1226 static void set_control_char_or_die(const struct control_info *info,
1227 const char *arg, struct termios *mode)
1229 unsigned char value;
1231 if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
1232 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1233 else if (arg[0] == '\0' || arg[1] == '\0')
1235 else if (strcmp(arg, "^-") == 0 || strcmp(arg, "undef") == 0)
1236 value = _POSIX_VDISABLE;
1237 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1238 value = arg[1] & 0x1f; /* Non-letters get weird results */
1242 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1243 mode->c_cc[info->offset] = value;
1246 #define STTY_require_set_attr (1 << 0)
1247 #define STTY_speed_was_set (1 << 1)
1248 #define STTY_verbose_output (1 << 2)
1249 #define STTY_recoverable_output (1 << 3)
1250 #define STTY_noargs (1 << 4)
1252 int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1253 int stty_main(int argc UNUSED_PARAM, char **argv)
1255 struct termios mode;
1256 void (*output_func)(const struct termios *, int);
1257 const char *file_name = NULL;
1258 int display_all = 0;
1264 stty_state = STTY_noargs;
1265 output_func = do_display;
1267 /* First pass: only parse/verify command line params */
1270 const struct mode_info *mp;
1271 const struct control_info *cp;
1272 const char *arg = argv[k];
1273 const char *argnext = argv[k+1];
1276 if (arg[0] == '-') {
1278 mp = find_mode(arg+1);
1280 if (!(mp->flags & REV))
1281 goto invalid_argument;
1282 stty_state &= ~STTY_noargs;
1285 /* It is an option - parse it */
1290 stty_state |= STTY_verbose_output;
1291 output_func = do_display;
1295 stty_state |= STTY_recoverable_output;
1296 output_func = display_recoverable;
1300 bb_error_msg_and_die("only one device may be specified");
1301 file_name = &arg[i+1]; /* "-Fdevice" ? */
1302 if (!file_name[0]) { /* nope, "-F device" */
1303 int p = k+1; /* argv[p] is argnext */
1304 file_name = argnext;
1306 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1307 /* remove -F param from arg[vc] */
1309 argv[p] = argv[p+1];
1315 goto invalid_argument;
1322 mp = find_mode(arg);
1324 stty_state &= ~STTY_noargs;
1328 cp = find_control(arg);
1331 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1332 /* called for the side effect of xfunc death only */
1333 set_control_char_or_die(cp, argnext, &mode);
1334 stty_state &= ~STTY_noargs;
1339 param = find_param(arg);
1340 if (param & param_need_arg) {
1342 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1350 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1352 # endif /* else fall-through */
1358 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1365 /* called for the side effect of xfunc death only */
1366 set_speed_or_die(input_speed, argnext, &mode);
1369 /* called for the side effect of xfunc death only */
1370 set_speed_or_die(output_speed, argnext, &mode);
1373 if (recover_mode(arg, &mode) == 1) break;
1374 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1376 bb_error_msg_and_die("invalid argument '%s'", arg);
1378 stty_state &= ~STTY_noargs;
1381 /* Specifying both -a and -g is an error */
1382 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1383 (STTY_verbose_output | STTY_recoverable_output))
1384 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
1385 /* Specifying -a or -g with non-options is an error */
1386 if (!(stty_state & STTY_noargs)
1387 && (stty_state & (STTY_verbose_output | STTY_recoverable_output))
1389 bb_error_msg_and_die("modes may not be set when specifying an output style");
1392 /* Now it is safe to start doing things */
1394 G.device_name = file_name;
1395 xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO);
1396 ndelay_off(STDIN_FILENO);
1399 /* Initialize to all zeroes so there is no risk memcmp will report a
1400 spurious difference in an uninitialized portion of the structure */
1401 memset(&mode, 0, sizeof(mode));
1402 if (tcgetattr(STDIN_FILENO, &mode))
1403 perror_on_device_and_die("%s");
1405 if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
1406 get_terminal_width_height(STDOUT_FILENO, &G.max_col, NULL);
1407 output_func(&mode, display_all);
1408 return EXIT_SUCCESS;
1411 /* Second pass: perform actions */
1414 const struct mode_info *mp;
1415 const struct control_info *cp;
1416 const char *arg = argv[k];
1417 const char *argnext = argv[k+1];
1420 if (arg[0] == '-') {
1421 mp = find_mode(arg+1);
1423 set_mode(mp, 1 /* reversed */, &mode);
1424 stty_state |= STTY_require_set_attr;
1426 /* It is an option - already parsed. Skip it */
1430 mp = find_mode(arg);
1432 set_mode(mp, 0 /* non-reversed */, &mode);
1433 stty_state |= STTY_require_set_attr;
1437 cp = find_control(arg);
1440 set_control_char_or_die(cp, argnext, &mode);
1441 stty_state |= STTY_require_set_attr;
1445 param = find_param(arg);
1446 if (param & param_need_arg) {
1453 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1454 stty_state |= STTY_require_set_attr;
1460 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1463 display_window_size(0);
1466 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1470 display_speed(&mode, 0);
1473 set_speed_or_die(input_speed, argnext, &mode);
1474 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1477 set_speed_or_die(output_speed, argnext, &mode);
1478 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1481 if (recover_mode(arg, &mode) == 1)
1482 stty_state |= STTY_require_set_attr;
1483 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
1484 set_speed_or_die(both_speeds, arg, &mode);
1485 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1486 } /* else - impossible (caught in the first pass):
1487 bb_error_msg_and_die("invalid argument '%s'", arg); */
1491 if (stty_state & STTY_require_set_attr) {
1492 struct termios new_mode;
1494 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1495 perror_on_device_and_die("%s");
1497 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1498 it performs *any* of the requested operations. This means it
1499 can report 'success' when it has actually failed to perform
1500 some proper subset of the requested operations. To detect
1501 this partial failure, get the current terminal attributes and
1502 compare them to the requested ones */
1504 /* Initialize to all zeroes so there is no risk memcmp will report a
1505 spurious difference in an uninitialized portion of the structure */
1506 memset(&new_mode, 0, sizeof(new_mode));
1507 if (tcgetattr(STDIN_FILENO, &new_mode))
1508 perror_on_device_and_die("%s");
1510 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1512 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1513 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1514 sometimes (m1 != m2). The only difference is in the four bits
1515 of the c_cflag field corresponding to the baud rate. To save
1516 Sun users a little confusion, don't report an error if this
1517 happens. But suppress the error only if we haven't tried to
1518 set the baud rate explicitly -- otherwise we'd never give an
1519 error for a true failure to set the baud rate */
1521 new_mode.c_cflag &= (~CIBAUD);
1522 if ((stty_state & STTY_speed_was_set)
1523 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1525 perror_on_device_and_die("%s: cannot perform all requested operations");
1529 return EXIT_SUCCESS;