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"
36 #ifndef _POSIX_VDISABLE
37 # define _POSIX_VDISABLE ((unsigned char) 0)
40 #define Control(c) ((c) & 0x1f)
41 /* Canonical values for control characters */
43 # define CINTR Control('c')
52 # define CKILL Control('u')
55 # define CEOF Control('d')
58 # define CEOL _POSIX_VDISABLE
61 # define CSTART Control('q')
64 # define CSTOP Control('s')
67 # define CSUSP Control('z')
69 #if defined(VEOL2) && !defined(CEOL2)
70 # define CEOL2 _POSIX_VDISABLE
72 /* glibc-2.12.1 uses only VSWTC name */
73 #if defined(VSWTC) && !defined(VSWTCH)
76 /* ISC renamed swtch to susp for termios, but we'll accept either name */
77 #if defined(VSUSP) && !defined(VSWTCH)
81 #if defined(VSWTCH) && !defined(CSWTCH)
82 # define CSWTCH _POSIX_VDISABLE
85 /* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'.
86 So the default is to disable 'swtch.' */
87 #if defined(__sparc__) && defined(__svr4__)
89 # define CSWTCH _POSIX_VDISABLE
92 #if defined(VWERSE) && !defined(VWERASE) /* AIX-3.2.5 */
93 # define VWERASE VWERSE
95 #if defined(VDSUSP) && !defined(CDSUSP)
96 # define CDSUSP Control('y')
98 #if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */
99 # define VREPRINT VRPRNT
101 #if defined(VREPRINT) && !defined(CRPRNT)
102 # define CRPRNT Control('r')
104 #if defined(VWERASE) && !defined(CWERASE)
105 # define CWERASE Control('w')
107 #if defined(VLNEXT) && !defined(CLNEXT)
108 # define CLNEXT Control('v')
110 #if defined(VDISCARD) && !defined(VFLUSHO)
111 # define VFLUSHO VDISCARD
113 #if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */
114 # define VFLUSHO VFLUSH
116 #if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */
117 # define ECHOCTL CTLECH
119 #if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */
120 # define ECHOCTL TCTLECH
122 #if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */
123 # define ECHOKE CRTKIL
125 #if defined(VFLUSHO) && !defined(CFLUSHO)
126 # define CFLUSHO Control('o')
128 #if defined(VSTATUS) && !defined(CSTATUS)
129 # define CSTATUS Control('t')
132 /* Save us from #ifdef forest plague */
242 /* Which speeds to set */
244 input_speed, output_speed, both_speeds
247 /* Which member(s) of 'struct termios' a mode uses */
249 /* Do NOT change the order or values, as mode_type_flag()
251 control, input, output, local, combination
254 /* Flags for 'struct mode_info' */
255 #define SANE_SET 1 /* Set in 'sane' mode */
256 #define SANE_UNSET 2 /* Unset in 'sane' mode */
257 #define REV 4 /* Can be turned off by prepending '-' */
258 #define OMIT 8 /* Don't display value */
262 * This structure should be kept as small as humanly possible.
265 const uint8_t type; /* Which structure element to change */
266 const uint8_t flags; /* Setting and display options */
267 /* only these values are ever used, so... */
268 #if (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100
270 #elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000
273 const tcflag_t mask; /* Other bits to turn off for this mode */
275 /* was using short here, but ppc32 was unhappy */
276 const tcflag_t bits; /* Bits to set for this mode */
280 /* Must match mode_name[] and mode_info[] order! */
300 #if XCASE && IUCLC && OLCUC
306 #define MI_ENTRY(N,T,F,B,M) N "\0"
308 /* Mode names given on command line */
309 static const char mode_name[] =
310 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
311 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
312 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
313 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
314 MI_ENTRY("ek", combination, OMIT, 0, 0 )
315 MI_ENTRY("sane", combination, OMIT, 0, 0 )
316 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
317 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
318 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
319 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
320 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
321 MI_ENTRY("crt", combination, OMIT, 0, 0 )
322 MI_ENTRY("dec", combination, OMIT, 0, 0 )
324 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
327 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
329 #if XCASE && IUCLC && OLCUC
330 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 )
331 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 )
333 MI_ENTRY("parenb", control, REV, PARENB, 0 )
334 MI_ENTRY("parodd", control, REV, PARODD, 0 )
335 MI_ENTRY("cs5", control, 0, CS5, CSIZE)
336 MI_ENTRY("cs6", control, 0, CS6, CSIZE)
337 MI_ENTRY("cs7", control, 0, CS7, CSIZE)
338 MI_ENTRY("cs8", control, 0, CS8, CSIZE)
339 MI_ENTRY("hupcl", control, REV, HUPCL, 0 )
340 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 )
341 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 )
342 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 )
343 MI_ENTRY("clocal", control, REV, CLOCAL, 0 )
345 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
347 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 )
348 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 )
349 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 )
350 MI_ENTRY("parmrk", input, REV, PARMRK, 0 )
351 MI_ENTRY("inpck", input, REV, INPCK, 0 )
352 MI_ENTRY("istrip", input, REV, ISTRIP, 0 )
353 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 )
354 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 )
355 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
356 MI_ENTRY("ixon", input, REV, IXON, 0 )
357 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
358 MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 )
360 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
363 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
366 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
369 MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 )
371 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
373 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
376 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
379 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
382 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
385 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
388 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
391 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
394 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
395 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
398 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY)
399 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY)
400 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY)
401 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY)
405 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY)
407 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY)
410 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY)
412 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY)
415 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
420 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
421 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
424 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
425 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
428 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
429 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
431 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
432 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
434 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
436 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
437 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
438 MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 )
439 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
440 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
441 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
443 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
446 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
449 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
450 MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 )
453 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
454 MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 )
457 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
458 MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 )
463 #define MI_ENTRY(N,T,F,B,M) { T, F, M, B },
465 static const struct mode_info mode_info[] = {
466 /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */
467 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
468 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
469 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
470 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
471 MI_ENTRY("ek", combination, OMIT, 0, 0 )
472 MI_ENTRY("sane", combination, OMIT, 0, 0 )
473 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
474 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
475 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
476 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
477 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
478 MI_ENTRY("crt", combination, OMIT, 0, 0 )
479 MI_ENTRY("dec", combination, OMIT, 0, 0 )
481 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
484 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
486 #if XCASE && IUCLC && OLCUC
487 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 )
488 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 )
490 MI_ENTRY("parenb", control, REV, PARENB, 0 )
491 MI_ENTRY("parodd", control, REV, PARODD, 0 )
492 MI_ENTRY("cs5", control, 0, CS5, CSIZE)
493 MI_ENTRY("cs6", control, 0, CS6, CSIZE)
494 MI_ENTRY("cs7", control, 0, CS7, CSIZE)
495 MI_ENTRY("cs8", control, 0, CS8, CSIZE)
496 MI_ENTRY("hupcl", control, REV, HUPCL, 0 )
497 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 )
498 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 )
499 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 )
500 MI_ENTRY("clocal", control, REV, CLOCAL, 0 )
502 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
504 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 )
505 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 )
506 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 )
507 MI_ENTRY("parmrk", input, REV, PARMRK, 0 )
508 MI_ENTRY("inpck", input, REV, INPCK, 0 )
509 MI_ENTRY("istrip", input, REV, ISTRIP, 0 )
510 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 )
511 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 )
512 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
513 MI_ENTRY("ixon", input, REV, IXON, 0 )
514 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
515 MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 )
517 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
520 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
523 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
526 MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 )
528 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
530 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
533 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
536 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
539 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
542 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
545 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
548 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
551 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
552 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
555 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY)
556 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY)
557 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY)
558 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY)
562 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY)
564 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY)
567 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY)
569 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY)
572 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
577 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
578 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
581 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
582 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
585 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
586 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
588 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
589 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
591 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
593 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
594 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
595 MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 )
596 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
597 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
598 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
600 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
603 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
606 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
607 MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 )
610 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
611 MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 )
614 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
615 MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 )
620 NUM_mode_info = ARRAY_SIZE(mode_info)
624 /* Control characters */
625 struct control_info {
626 const uint8_t saneval; /* Value to set for 'stty sane' */
627 const uint8_t offset; /* Offset in c_cc */
631 /* Must match control_name[] and control_info[] order! */
669 #define CI_ENTRY(n,s,o) n "\0"
671 /* Name given on command line */
672 static const char control_name[] =
673 CI_ENTRY("intr", CINTR, VINTR )
674 CI_ENTRY("quit", CQUIT, VQUIT )
675 CI_ENTRY("erase", CERASE, VERASE )
676 CI_ENTRY("kill", CKILL, VKILL )
677 CI_ENTRY("eof", CEOF, VEOF )
678 CI_ENTRY("eol", CEOL, VEOL )
680 CI_ENTRY("eol2", CEOL2, VEOL2 )
683 CI_ENTRY("swtch", CSWTCH, VSWTCH )
685 CI_ENTRY("start", CSTART, VSTART )
686 CI_ENTRY("stop", CSTOP, VSTOP )
687 CI_ENTRY("susp", CSUSP, VSUSP )
689 CI_ENTRY("dsusp", CDSUSP, VDSUSP )
692 CI_ENTRY("rprnt", CRPRNT, VREPRINT)
695 CI_ENTRY("werase", CWERASE, VWERASE )
698 CI_ENTRY("lnext", CLNEXT, VLNEXT )
701 CI_ENTRY("flush", CFLUSHO, VFLUSHO )
704 CI_ENTRY("status", CSTATUS, VSTATUS )
706 /* These must be last because of the display routines */
707 CI_ENTRY("min", 1, VMIN )
708 CI_ENTRY("time", 0, VTIME )
712 #define CI_ENTRY(n,s,o) { s, o },
714 static const struct control_info control_info[] = {
715 /* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */
716 CI_ENTRY("intr", CINTR, VINTR )
717 CI_ENTRY("quit", CQUIT, VQUIT )
718 CI_ENTRY("erase", CERASE, VERASE )
719 CI_ENTRY("kill", CKILL, VKILL )
720 CI_ENTRY("eof", CEOF, VEOF )
721 CI_ENTRY("eol", CEOL, VEOL )
723 CI_ENTRY("eol2", CEOL2, VEOL2 )
726 CI_ENTRY("swtch", CSWTCH, VSWTCH )
728 CI_ENTRY("start", CSTART, VSTART )
729 CI_ENTRY("stop", CSTOP, VSTOP )
730 CI_ENTRY("susp", CSUSP, VSUSP )
732 CI_ENTRY("dsusp", CDSUSP, VDSUSP )
735 CI_ENTRY("rprnt", CRPRNT, VREPRINT)
738 CI_ENTRY("werase", CWERASE, VWERASE )
741 CI_ENTRY("lnext", CLNEXT, VLNEXT )
744 CI_ENTRY("flush", CFLUSHO, VFLUSHO )
747 CI_ENTRY("status", CSTATUS, VSTATUS )
749 /* These must be last because of the display routines */
750 CI_ENTRY("min", 1, VMIN )
751 CI_ENTRY("time", 0, VTIME )
755 NUM_control_info = ARRAY_SIZE(control_info)
760 const char *device_name;
761 /* The width of the screen, for output wrapping */
763 /* Current position, to know when to wrap */
764 unsigned current_col;
767 #define G (*(struct globals*)&bb_common_bufsiz1)
768 #define INIT_G() do { \
769 G.device_name = bb_msg_standard_input; \
774 /* Return a string that is the printable representation of character CH */
775 /* Adapted from 'cat' by Torbjorn Granlund */
776 static const char *visible(unsigned ch)
780 if (ch == _POSIX_VDISABLE)
792 } else if (ch < 127) {
803 static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
805 static const uint8_t tcflag_offsets[] ALIGN1 = {
806 offsetof(struct termios, c_cflag), /* control */
807 offsetof(struct termios, c_iflag), /* input */
808 offsetof(struct termios, c_oflag), /* output */
809 offsetof(struct termios, c_lflag) /* local */
813 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
818 static void set_speed_or_die(enum speed_setting type, const char *arg,
819 struct termios *mode)
823 baud = tty_value_to_baud(xatou(arg));
825 if (type != output_speed) { /* either input or both */
826 cfsetispeed(mode, baud);
828 if (type != input_speed) { /* either output or both */
829 cfsetospeed(mode, baud);
833 static NORETURN void perror_on_device_and_die(const char *fmt)
835 bb_perror_msg_and_die(fmt, G.device_name);
838 static void perror_on_device(const char *fmt)
840 bb_perror_msg(fmt, G.device_name);
843 /* Print format string MESSAGE and optional args.
844 Wrap to next line first if it won't fit.
845 Print a space first unless MESSAGE will start a new line */
846 static void wrapf(const char *message, ...)
852 va_start(args, message);
853 buflen = vsnprintf(buf, sizeof(buf), message, args);
855 /* We seem to be called only with suitable lengths, but check if
856 somebody failed to adhere to this assumption just to be sure. */
857 if (!buflen || buflen >= sizeof(buf)) return;
859 if (G.current_col > 0) {
861 if (buf[0] != '\n') {
862 if (G.current_col + buflen >= G.max_col) {
870 G.current_col += buflen;
871 if (buf[buflen-1] == '\n')
875 static void newline(void)
877 if (G.current_col != 0)
882 static void set_window_size(int rows, int cols)
884 struct winsize win = { 0, 0, 0, 0 };
886 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
887 if (errno != EINVAL) {
890 memset(&win, 0, sizeof(win));
898 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
900 perror_on_device("%s");
904 static void display_window_size(int fancy)
906 const char *fmt_str = "%s\0%s: no size information for this device";
907 unsigned width, height;
909 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
910 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
911 perror_on_device(fmt_str);
914 wrapf(fancy ? "rows %u; columns %u;" : "%u %u\n",
919 static const struct suffix_mult stty_suffixes[] = {
926 static const struct mode_info *find_mode(const char *name)
928 int i = index_in_strings(mode_name, name);
929 return i >= 0 ? &mode_info[i] : NULL;
932 static const struct control_info *find_control(const char *name)
934 int i = index_in_strings(control_name, name);
935 return i >= 0 ? &control_info[i] : NULL;
939 param_need_arg = 0x80,
940 param_line = 1 | 0x80,
941 param_rows = 2 | 0x80,
942 param_cols = 3 | 0x80,
943 param_columns = 4 | 0x80,
946 param_ispeed = 7 | 0x80,
947 param_ospeed = 8 | 0x80,
950 static int find_param(const char *name)
952 static const char params[] ALIGN1 =
961 int i = index_in_strings(params, name) + 1;
964 if (i != 5 && i != 6)
969 static int recover_mode(const char *arg, struct termios *mode)
973 unsigned long iflag, oflag, cflag, lflag;
975 /* Scan into temporaries since it is too much trouble to figure out
976 the right format for 'tcflag_t' */
977 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
978 &iflag, &oflag, &cflag, &lflag, &n) != 4)
980 mode->c_iflag = iflag;
981 mode->c_oflag = oflag;
982 mode->c_cflag = cflag;
983 mode->c_lflag = lflag;
985 for (i = 0; i < NCCS; ++i) {
986 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
992 /* Fail if there are too many fields */
999 static void display_recoverable(const struct termios *mode,
1000 int UNUSED_PARAM dummy)
1003 printf("%lx:%lx:%lx:%lx",
1004 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1005 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1006 for (i = 0; i < NCCS; ++i)
1007 printf(":%x", (unsigned int) mode->c_cc[i]);
1011 static void display_speed(const struct termios *mode, int fancy)
1013 //____________________ 01234567 8 9
1014 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
1015 unsigned long ispeed, ospeed;
1017 ispeed = cfgetispeed(mode);
1018 ospeed = cfgetospeed(mode);
1019 if (ispeed == 0 || ispeed == ospeed) {
1020 ispeed = ospeed; /* in case ispeed was 0 */
1021 //________ 0123 4 5 6 7 8 9
1022 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
1024 if (fancy) fmt_str += 9;
1025 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
1028 static void do_display(const struct termios *mode, int all)
1033 int prev_type = control;
1035 display_speed(mode, 1);
1037 display_window_size(1);
1039 wrapf("line = %u;\n", mode->c_line);
1044 for (i = 0; i != CIDX_min; ++i) {
1045 /* If swtch is the same as susp, don't print both */
1047 if (i == CIDX_swtch)
1050 /* If eof uses the same slot as min, only print whichever applies */
1052 if (!(mode->c_lflag & ICANON)
1053 && (i == CIDX_eof || i == CIDX_eol)
1058 wrapf("%s = %s;", nth_string(control_name, i),
1059 visible(mode->c_cc[control_info[i].offset]));
1062 if ((mode->c_lflag & ICANON) == 0)
1064 wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1067 for (i = 0; i < NUM_mode_info; ++i) {
1068 if (mode_info[i].flags & OMIT)
1070 if (mode_info[i].type != prev_type) {
1072 prev_type = mode_info[i].type;
1075 bitsp = mode_type_flag(mode_info[i].type, mode);
1076 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1077 if ((*bitsp & mask) == mode_info[i].bits) {
1078 if (all || (mode_info[i].flags & SANE_UNSET))
1079 wrapf("-%s"+1, nth_string(mode_name, i));
1081 if ((all && mode_info[i].flags & REV)
1082 || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
1084 wrapf("-%s", nth_string(mode_name, i));
1091 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) {
1105 if (mode_info[i].flags & SANE_SET) {
1106 bitsp = mode_type_flag(mode_info[i].type, mode);
1107 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
1108 | mode_info[i].bits;
1109 } else if (mode_info[i].flags & SANE_UNSET) {
1110 bitsp = mode_type_flag(mode_info[i].type, mode);
1111 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
1112 & ~mode_info[i].bits;
1117 static void set_mode(const struct mode_info *info, int reversed,
1118 struct termios *mode)
1122 bitsp = mode_type_flag(info->type, mode);
1126 *bitsp = *bitsp & ~info->mask & ~info->bits;
1128 *bitsp = (*bitsp & ~info->mask) | info->bits;
1132 /* Combination mode */
1133 if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
1135 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1137 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
1138 } else if (info == &mode_info[IDX_oddp]) {
1140 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1142 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
1143 } else if (info == &mode_info[IDX_nl]) {
1145 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
1146 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
1148 mode->c_iflag = mode->c_iflag & ~ICRNL;
1149 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1151 } else if (info == &mode_info[IDX_ek]) {
1152 mode->c_cc[VERASE] = CERASE;
1153 mode->c_cc[VKILL] = CKILL;
1154 } else if (info == &mode_info[IDX_sane]) {
1156 } else if (info == &mode_info[IDX_cbreak]) {
1158 mode->c_lflag |= ICANON;
1160 mode->c_lflag &= ~ICANON;
1161 } else if (info == &mode_info[IDX_pass8]) {
1163 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1164 mode->c_iflag |= ISTRIP;
1166 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1167 mode->c_iflag &= ~ISTRIP;
1169 } else if (info == &mode_info[IDX_litout]) {
1171 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1172 mode->c_iflag |= ISTRIP;
1173 mode->c_oflag |= OPOST;
1175 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1176 mode->c_iflag &= ~ISTRIP;
1177 mode->c_oflag &= ~OPOST;
1179 } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
1180 if ((info == &mode_info[IDX_raw] && reversed)
1181 || (info == &mode_info[IDX_cooked] && !reversed)
1184 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1185 mode->c_oflag |= OPOST;
1186 mode->c_lflag |= ISIG | ICANON;
1188 mode->c_cc[VEOF] = CEOF;
1191 mode->c_cc[VEOL] = CEOL;
1196 mode->c_oflag &= ~OPOST;
1197 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1198 mode->c_cc[VMIN] = 1;
1199 mode->c_cc[VTIME] = 0;
1203 else if (info == &mode_info[IDX_decctlq]) {
1205 mode->c_iflag |= IXANY;
1207 mode->c_iflag &= ~IXANY;
1211 else if (info == &mode_info[IDX_tabs]) {
1213 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1215 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1219 else if (info == &mode_info[IDX_tabs]) {
1221 mode->c_oflag |= OXTABS;
1223 mode->c_oflag &= ~OXTABS;
1226 #if XCASE && IUCLC && OLCUC
1227 else if (info==&mode_info[IDX_lcase] || info==&mode_info[IDX_LCASE]) {
1229 mode->c_lflag &= ~XCASE;
1230 mode->c_iflag &= ~IUCLC;
1231 mode->c_oflag &= ~OLCUC;
1233 mode->c_lflag |= XCASE;
1234 mode->c_iflag |= IUCLC;
1235 mode->c_oflag |= OLCUC;
1239 else if (info == &mode_info[IDX_crt]) {
1240 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1241 } else if (info == &mode_info[IDX_dec]) {
1242 mode->c_cc[VINTR] = 3; /* ^C */
1243 mode->c_cc[VERASE] = 127; /* DEL */
1244 mode->c_cc[VKILL] = 21; /* ^U */
1245 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1246 if (IXANY) mode->c_iflag &= ~IXANY;
1250 static void set_control_char_or_die(const struct control_info *info,
1251 const char *arg, struct termios *mode)
1253 unsigned char value;
1255 if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
1256 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1257 else if (arg[0] == '\0' || arg[1] == '\0')
1259 else if (strcmp(arg, "^-") == 0 || strcmp(arg, "undef") == 0)
1260 value = _POSIX_VDISABLE;
1261 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1262 value = arg[1] & 0x1f; /* Non-letters get weird results */
1266 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1267 mode->c_cc[info->offset] = value;
1270 #define STTY_require_set_attr (1 << 0)
1271 #define STTY_speed_was_set (1 << 1)
1272 #define STTY_verbose_output (1 << 2)
1273 #define STTY_recoverable_output (1 << 3)
1274 #define STTY_noargs (1 << 4)
1276 int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1277 int stty_main(int argc UNUSED_PARAM, char **argv)
1279 struct termios mode;
1280 void (*output_func)(const struct termios *, int);
1281 const char *file_name = NULL;
1282 int display_all = 0;
1288 stty_state = STTY_noargs;
1289 output_func = do_display;
1291 /* First pass: only parse/verify command line params */
1294 const struct mode_info *mp;
1295 const struct control_info *cp;
1296 const char *arg = argv[k];
1297 const char *argnext = argv[k+1];
1300 if (arg[0] == '-') {
1302 mp = find_mode(arg+1);
1304 if (!(mp->flags & REV))
1305 goto invalid_argument;
1306 stty_state &= ~STTY_noargs;
1309 /* It is an option - parse it */
1314 stty_state |= STTY_verbose_output;
1315 output_func = do_display;
1319 stty_state |= STTY_recoverable_output;
1320 output_func = display_recoverable;
1324 bb_error_msg_and_die("only one device may be specified");
1325 file_name = &arg[i+1]; /* "-Fdevice" ? */
1326 if (!file_name[0]) { /* nope, "-F device" */
1327 int p = k+1; /* argv[p] is argnext */
1328 file_name = argnext;
1330 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1331 /* remove -F param from arg[vc] */
1333 argv[p] = argv[p+1];
1339 goto invalid_argument;
1346 mp = find_mode(arg);
1348 stty_state &= ~STTY_noargs;
1352 cp = find_control(arg);
1355 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1356 /* called for the side effect of xfunc death only */
1357 set_control_char_or_die(cp, argnext, &mode);
1358 stty_state &= ~STTY_noargs;
1363 param = find_param(arg);
1364 if (param & param_need_arg) {
1366 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1374 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1376 # endif /* else fall-through */
1382 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1389 /* called for the side effect of xfunc death only */
1390 set_speed_or_die(input_speed, argnext, &mode);
1393 /* called for the side effect of xfunc death only */
1394 set_speed_or_die(output_speed, argnext, &mode);
1397 if (recover_mode(arg, &mode) == 1) break;
1398 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1400 bb_error_msg_and_die("invalid argument '%s'", arg);
1402 stty_state &= ~STTY_noargs;
1405 /* Specifying both -a and -g is an error */
1406 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1407 (STTY_verbose_output | STTY_recoverable_output))
1408 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
1409 /* Specifying -a or -g with non-options is an error */
1410 if (!(stty_state & STTY_noargs)
1411 && (stty_state & (STTY_verbose_output | STTY_recoverable_output))
1413 bb_error_msg_and_die("modes may not be set when specifying an output style");
1416 /* Now it is safe to start doing things */
1418 G.device_name = file_name;
1419 xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO);
1420 ndelay_off(STDIN_FILENO);
1423 /* Initialize to all zeroes so there is no risk memcmp will report a
1424 spurious difference in an uninitialized portion of the structure */
1425 memset(&mode, 0, sizeof(mode));
1426 if (tcgetattr(STDIN_FILENO, &mode))
1427 perror_on_device_and_die("%s");
1429 if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
1430 get_terminal_width_height(STDOUT_FILENO, &G.max_col, NULL);
1431 output_func(&mode, display_all);
1432 return EXIT_SUCCESS;
1435 /* Second pass: perform actions */
1438 const struct mode_info *mp;
1439 const struct control_info *cp;
1440 const char *arg = argv[k];
1441 const char *argnext = argv[k+1];
1444 if (arg[0] == '-') {
1445 mp = find_mode(arg+1);
1447 set_mode(mp, 1 /* reversed */, &mode);
1448 stty_state |= STTY_require_set_attr;
1450 /* It is an option - already parsed. Skip it */
1454 mp = find_mode(arg);
1456 set_mode(mp, 0 /* non-reversed */, &mode);
1457 stty_state |= STTY_require_set_attr;
1461 cp = find_control(arg);
1464 set_control_char_or_die(cp, argnext, &mode);
1465 stty_state |= STTY_require_set_attr;
1469 param = find_param(arg);
1470 if (param & param_need_arg) {
1477 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1478 stty_state |= STTY_require_set_attr;
1484 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1487 display_window_size(0);
1490 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1494 display_speed(&mode, 0);
1497 set_speed_or_die(input_speed, argnext, &mode);
1498 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1501 set_speed_or_die(output_speed, argnext, &mode);
1502 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1505 if (recover_mode(arg, &mode) == 1)
1506 stty_state |= STTY_require_set_attr;
1507 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
1508 set_speed_or_die(both_speeds, arg, &mode);
1509 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1510 } /* else - impossible (caught in the first pass):
1511 bb_error_msg_and_die("invalid argument '%s'", arg); */
1515 if (stty_state & STTY_require_set_attr) {
1516 struct termios new_mode;
1518 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1519 perror_on_device_and_die("%s");
1521 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1522 it performs *any* of the requested operations. This means it
1523 can report 'success' when it has actually failed to perform
1524 some proper subset of the requested operations. To detect
1525 this partial failure, get the current terminal attributes and
1526 compare them to the requested ones */
1528 /* Initialize to all zeroes so there is no risk memcmp will report a
1529 spurious difference in an uninitialized portion of the structure */
1530 memset(&new_mode, 0, sizeof(new_mode));
1531 if (tcgetattr(STDIN_FILENO, &new_mode))
1532 perror_on_device_and_die("%s");
1534 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1536 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1537 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1538 sometimes (m1 != m2). The only difference is in the four bits
1539 of the c_cflag field corresponding to the baud rate. To save
1540 Sun users a little confusion, don't report an error if this
1541 happens. But suppress the error only if we haven't tried to
1542 set the baud rate explicitly -- otherwise we'd never give an
1543 error for a true failure to set the baud rate */
1545 new_mode.c_cflag &= (~CIBAUD);
1546 if ((stty_state & STTY_speed_was_set)
1547 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1549 perror_on_device_and_die("%s: cannot perform all requested operations");
1553 return EXIT_SUCCESS;