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 control, input, output, local, combination
251 static tcflag_t *get_ptr_to_tcflag(unsigned type, const struct termios *mode)
253 static const uint8_t tcflag_offsets[] ALIGN1 = {
254 offsetof(struct termios, c_cflag), /* control */
255 offsetof(struct termios, c_iflag), /* input */
256 offsetof(struct termios, c_oflag), /* output */
257 offsetof(struct termios, c_lflag) /* local */
260 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
265 /* Flags for 'struct mode_info' */
266 #define SANE_SET 1 /* Set in 'sane' mode */
267 #define SANE_UNSET 2 /* Unset in 'sane' mode */
268 #define REV 4 /* Can be turned off by prepending '-' */
269 #define OMIT 8 /* Don't display value */
273 * This structure should be kept as small as humanly possible.
276 const uint8_t type; /* Which structure element to change */
277 const uint8_t flags; /* Setting and display options */
278 /* only these values are ever used, so... */
279 #if (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100
281 #elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000
284 const tcflag_t mask; /* Other bits to turn off for this mode */
286 /* was using short here, but ppc32 was unhappy */
287 const tcflag_t bits; /* Bits to set for this mode */
291 /* Must match mode_name[] and mode_info[] order! */
311 #if XCASE && IUCLC && OLCUC
317 #define MI_ENTRY(N,T,F,B,M) N "\0"
319 /* Mode names given on command line */
320 static const char mode_name[] =
321 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
322 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
323 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
324 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
325 MI_ENTRY("ek", combination, OMIT, 0, 0 )
326 MI_ENTRY("sane", combination, OMIT, 0, 0 )
327 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
328 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
329 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
330 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
331 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
332 MI_ENTRY("crt", combination, OMIT, 0, 0 )
333 MI_ENTRY("dec", combination, OMIT, 0, 0 )
335 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
338 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
340 #if XCASE && IUCLC && OLCUC
341 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 )
342 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 )
344 MI_ENTRY("parenb", control, REV, PARENB, 0 )
345 MI_ENTRY("parodd", control, REV, PARODD, 0 )
346 MI_ENTRY("cs5", control, 0, CS5, CSIZE)
347 MI_ENTRY("cs6", control, 0, CS6, CSIZE)
348 MI_ENTRY("cs7", control, 0, CS7, CSIZE)
349 MI_ENTRY("cs8", control, 0, CS8, CSIZE)
350 MI_ENTRY("hupcl", control, REV, HUPCL, 0 )
351 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 )
352 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 )
353 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 )
354 MI_ENTRY("clocal", control, REV, CLOCAL, 0 )
356 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
358 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 )
359 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 )
360 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 )
361 MI_ENTRY("parmrk", input, REV, PARMRK, 0 )
362 MI_ENTRY("inpck", input, REV, INPCK, 0 )
363 MI_ENTRY("istrip", input, REV, ISTRIP, 0 )
364 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 )
365 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 )
366 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
367 MI_ENTRY("ixon", input, REV, IXON, 0 )
368 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
369 MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 )
371 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
374 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
377 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
380 MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 )
382 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
384 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
387 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
390 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
393 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
396 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
399 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
402 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
405 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
406 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
409 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY)
410 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY)
411 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY)
412 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY)
416 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY)
418 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY)
421 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY)
423 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY)
426 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
431 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
432 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
435 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
436 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
439 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
440 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
442 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
443 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
445 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
447 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
448 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
449 MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 )
450 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
451 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
452 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
454 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
457 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
460 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
461 MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 )
464 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
465 MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 )
468 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
469 MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 )
474 #define MI_ENTRY(N,T,F,B,M) { T, F, M, B },
476 static const struct mode_info mode_info[] = {
477 /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */
478 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
479 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
480 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
481 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
482 MI_ENTRY("ek", combination, OMIT, 0, 0 )
483 MI_ENTRY("sane", combination, OMIT, 0, 0 )
484 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
485 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
486 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
487 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
488 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
489 MI_ENTRY("crt", combination, OMIT, 0, 0 )
490 MI_ENTRY("dec", combination, OMIT, 0, 0 )
492 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
495 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
497 #if XCASE && IUCLC && OLCUC
498 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 )
499 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 )
501 MI_ENTRY("parenb", control, REV, PARENB, 0 )
502 MI_ENTRY("parodd", control, REV, PARODD, 0 )
503 MI_ENTRY("cs5", control, 0, CS5, CSIZE)
504 MI_ENTRY("cs6", control, 0, CS6, CSIZE)
505 MI_ENTRY("cs7", control, 0, CS7, CSIZE)
506 MI_ENTRY("cs8", control, 0, CS8, CSIZE)
507 MI_ENTRY("hupcl", control, REV, HUPCL, 0 )
508 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 )
509 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 )
510 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 )
511 MI_ENTRY("clocal", control, REV, CLOCAL, 0 )
513 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
515 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 )
516 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 )
517 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 )
518 MI_ENTRY("parmrk", input, REV, PARMRK, 0 )
519 MI_ENTRY("inpck", input, REV, INPCK, 0 )
520 MI_ENTRY("istrip", input, REV, ISTRIP, 0 )
521 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 )
522 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 )
523 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
524 MI_ENTRY("ixon", input, REV, IXON, 0 )
525 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
526 MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 )
528 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
531 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
534 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
537 MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 )
539 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
541 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
544 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
547 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
550 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
553 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
556 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
559 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
562 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
563 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
566 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY)
567 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY)
568 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY)
569 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY)
573 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY)
575 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY)
578 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY)
580 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY)
583 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
588 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
589 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
592 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
593 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
596 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
597 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
599 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
600 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
602 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
604 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
605 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
606 MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 )
607 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
608 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
609 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
611 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
614 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
617 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
618 MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 )
621 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
622 MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 )
625 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
626 MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 )
631 NUM_mode_info = ARRAY_SIZE(mode_info)
635 /* Control characters */
636 struct control_info {
637 const uint8_t saneval; /* Value to set for 'stty sane' */
638 const uint8_t offset; /* Offset in c_cc */
642 /* Must match control_name[] and control_info[] order! */
680 #define CI_ENTRY(n,s,o) n "\0"
682 /* Name given on command line */
683 static const char control_name[] =
684 CI_ENTRY("intr", CINTR, VINTR )
685 CI_ENTRY("quit", CQUIT, VQUIT )
686 CI_ENTRY("erase", CERASE, VERASE )
687 CI_ENTRY("kill", CKILL, VKILL )
688 CI_ENTRY("eof", CEOF, VEOF )
689 CI_ENTRY("eol", CEOL, VEOL )
691 CI_ENTRY("eol2", CEOL2, VEOL2 )
694 CI_ENTRY("swtch", CSWTCH, VSWTCH )
696 CI_ENTRY("start", CSTART, VSTART )
697 CI_ENTRY("stop", CSTOP, VSTOP )
698 CI_ENTRY("susp", CSUSP, VSUSP )
700 CI_ENTRY("dsusp", CDSUSP, VDSUSP )
703 CI_ENTRY("rprnt", CRPRNT, VREPRINT)
706 CI_ENTRY("werase", CWERASE, VWERASE )
709 CI_ENTRY("lnext", CLNEXT, VLNEXT )
712 CI_ENTRY("flush", CFLUSHO, VFLUSHO )
715 CI_ENTRY("status", CSTATUS, VSTATUS )
717 /* These must be last because of the display routines */
718 CI_ENTRY("min", 1, VMIN )
719 CI_ENTRY("time", 0, VTIME )
723 #define CI_ENTRY(n,s,o) { s, o },
725 static const struct control_info control_info[] = {
726 /* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */
727 CI_ENTRY("intr", CINTR, VINTR )
728 CI_ENTRY("quit", CQUIT, VQUIT )
729 CI_ENTRY("erase", CERASE, VERASE )
730 CI_ENTRY("kill", CKILL, VKILL )
731 CI_ENTRY("eof", CEOF, VEOF )
732 CI_ENTRY("eol", CEOL, VEOL )
734 CI_ENTRY("eol2", CEOL2, VEOL2 )
737 CI_ENTRY("swtch", CSWTCH, VSWTCH )
739 CI_ENTRY("start", CSTART, VSTART )
740 CI_ENTRY("stop", CSTOP, VSTOP )
741 CI_ENTRY("susp", CSUSP, VSUSP )
743 CI_ENTRY("dsusp", CDSUSP, VDSUSP )
746 CI_ENTRY("rprnt", CRPRNT, VREPRINT)
749 CI_ENTRY("werase", CWERASE, VWERASE )
752 CI_ENTRY("lnext", CLNEXT, VLNEXT )
755 CI_ENTRY("flush", CFLUSHO, VFLUSHO )
758 CI_ENTRY("status", CSTATUS, VSTATUS )
760 /* These must be last because of the display routines */
761 CI_ENTRY("min", 1, VMIN )
762 CI_ENTRY("time", 0, VTIME )
766 NUM_control_info = ARRAY_SIZE(control_info)
771 const char *device_name;
772 /* The width of the screen, for output wrapping */
774 /* Current position, to know when to wrap */
775 unsigned current_col;
778 #define G (*(struct globals*)&bb_common_bufsiz1)
779 #define INIT_G() do { \
780 G.device_name = bb_msg_standard_input; \
784 static void set_speed_or_die(enum speed_setting type, const char *arg,
785 struct termios *mode)
789 baud = tty_value_to_baud(xatou(arg));
791 if (type != output_speed) { /* either input or both */
792 cfsetispeed(mode, baud);
794 if (type != input_speed) { /* either output or both */
795 cfsetospeed(mode, baud);
799 static NORETURN void perror_on_device_and_die(const char *fmt)
801 bb_perror_msg_and_die(fmt, G.device_name);
804 static void perror_on_device(const char *fmt)
806 bb_perror_msg(fmt, G.device_name);
809 /* Print format string MESSAGE and optional args.
810 Wrap to next line first if it won't fit.
811 Print a space first unless MESSAGE will start a new line */
812 static void wrapf(const char *message, ...)
818 va_start(args, message);
819 buflen = vsnprintf(buf, sizeof(buf), message, args);
821 /* We seem to be called only with suitable lengths, but check if
822 somebody failed to adhere to this assumption just to be sure. */
823 if (!buflen || buflen >= sizeof(buf)) return;
825 if (G.current_col > 0) {
827 if (buf[0] != '\n') {
828 if (G.current_col + buflen >= G.max_col) {
836 G.current_col += buflen;
837 if (buf[buflen-1] == '\n')
841 static void newline(void)
843 if (G.current_col != 0)
848 static void set_window_size(int rows, int cols)
850 struct winsize win = { 0, 0, 0, 0 };
852 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
853 if (errno != EINVAL) {
856 memset(&win, 0, sizeof(win));
864 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
866 perror_on_device("%s");
870 static void display_window_size(int fancy)
872 const char *fmt_str = "%s\0%s: no size information for this device";
873 unsigned width, height;
875 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
876 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
877 perror_on_device(fmt_str);
880 wrapf(fancy ? "rows %u; columns %u;" : "%u %u\n",
885 static const struct suffix_mult stty_suffixes[] = {
892 static const struct mode_info *find_mode(const char *name)
894 int i = index_in_strings(mode_name, name);
895 return i >= 0 ? &mode_info[i] : NULL;
898 static const struct control_info *find_control(const char *name)
900 int i = index_in_strings(control_name, name);
901 return i >= 0 ? &control_info[i] : NULL;
905 param_need_arg = 0x80,
906 param_line = 1 | 0x80,
907 param_rows = 2 | 0x80,
908 param_cols = 3 | 0x80,
909 param_columns = 4 | 0x80,
912 param_ispeed = 7 | 0x80,
913 param_ospeed = 8 | 0x80,
916 static int find_param(const char *name)
918 static const char params[] ALIGN1 =
927 int i = index_in_strings(params, name) + 1;
930 if (i != 5 && i != 6)
935 static int recover_mode(const char *arg, struct termios *mode)
939 unsigned long iflag, oflag, cflag, lflag;
941 /* Scan into temporaries since it is too much trouble to figure out
942 the right format for 'tcflag_t' */
943 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
944 &iflag, &oflag, &cflag, &lflag, &n) != 4)
946 mode->c_iflag = iflag;
947 mode->c_oflag = oflag;
948 mode->c_cflag = cflag;
949 mode->c_lflag = lflag;
951 for (i = 0; i < NCCS; ++i) {
952 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
958 /* Fail if there are too many fields */
965 static void display_recoverable(const struct termios *mode,
966 int UNUSED_PARAM dummy)
969 printf("%lx:%lx:%lx:%lx",
970 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
971 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
972 for (i = 0; i < NCCS; ++i)
973 printf(":%x", (unsigned int) mode->c_cc[i]);
977 static void display_speed(const struct termios *mode, int fancy)
979 //____________________ 01234567 8 9
980 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
981 unsigned long ispeed, ospeed;
983 ispeed = cfgetispeed(mode);
984 ospeed = cfgetospeed(mode);
985 if (ispeed == 0 || ispeed == ospeed) {
986 ispeed = ospeed; /* in case ispeed was 0 */
987 //________ 0123 4 5 6 7 8 9
988 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
990 if (fancy) fmt_str += 9;
991 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
994 static void do_display(const struct termios *mode, int all)
999 int prev_type = control;
1001 display_speed(mode, 1);
1003 display_window_size(1);
1005 wrapf("line = %u;\n", mode->c_line);
1010 for (i = 0; i != CIDX_min; ++i) {
1012 /* If swtch is the same as susp, don't print both */
1014 if (i == CIDX_swtch)
1017 /* If eof uses the same slot as min, only print whichever applies */
1019 if (!(mode->c_lflag & ICANON)
1020 && (i == CIDX_eof || i == CIDX_eol)
1025 ch = mode->c_cc[control_info[i].offset];
1026 if (ch == _POSIX_VDISABLE)
1027 strcpy(G.buf, "<undef>");
1029 visible(ch, G.buf, 0);
1030 wrapf("%s = %s;", nth_string(control_name, i), G.buf);
1033 if ((mode->c_lflag & ICANON) == 0)
1035 wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1038 for (i = 0; i < NUM_mode_info; ++i) {
1039 if (mode_info[i].flags & OMIT)
1041 if (mode_info[i].type != prev_type) {
1043 prev_type = mode_info[i].type;
1046 bitsp = get_ptr_to_tcflag(mode_info[i].type, mode);
1047 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1048 if ((*bitsp & mask) == mode_info[i].bits) {
1049 if (all || (mode_info[i].flags & SANE_UNSET))
1050 wrapf("-%s"+1, nth_string(mode_name, i));
1052 if ((all && mode_info[i].flags & REV)
1053 || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
1055 wrapf("-%s", nth_string(mode_name, i));
1062 static void sane_mode(struct termios *mode)
1066 for (i = 0; i < NUM_control_info; ++i) {
1071 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1074 for (i = 0; i < NUM_mode_info; ++i) {
1076 tcflag_t *bitsp = get_ptr_to_tcflag(mode_info[i].type, mode);
1080 val = *bitsp & ~((unsigned long)mode_info[i].mask);
1081 if (mode_info[i].flags & SANE_SET) {
1082 *bitsp = val | mode_info[i].bits;
1084 if (mode_info[i].flags & SANE_UNSET) {
1085 *bitsp = val & ~mode_info[i].bits;
1090 static void set_mode(const struct mode_info *info, int reversed,
1091 struct termios *mode)
1095 bitsp = get_ptr_to_tcflag(info->type, mode);
1098 tcflag_t val = *bitsp & ~info->mask;
1100 *bitsp = val & ~info->bits;
1102 *bitsp = val | info->bits;
1106 /* !bitsp - it's a "combination" mode */
1107 if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
1109 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1111 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
1112 } else if (info == &mode_info[IDX_oddp]) {
1114 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1116 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
1117 } else if (info == &mode_info[IDX_nl]) {
1119 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
1120 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
1122 mode->c_iflag = mode->c_iflag & ~ICRNL;
1123 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1125 } else if (info == &mode_info[IDX_ek]) {
1126 mode->c_cc[VERASE] = CERASE;
1127 mode->c_cc[VKILL] = CKILL;
1128 } else if (info == &mode_info[IDX_sane]) {
1130 } else if (info == &mode_info[IDX_cbreak]) {
1132 mode->c_lflag |= ICANON;
1134 mode->c_lflag &= ~ICANON;
1135 } else if (info == &mode_info[IDX_pass8]) {
1137 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1138 mode->c_iflag |= ISTRIP;
1140 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1141 mode->c_iflag &= ~ISTRIP;
1143 } else if (info == &mode_info[IDX_litout]) {
1145 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1146 mode->c_iflag |= ISTRIP;
1147 mode->c_oflag |= OPOST;
1149 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1150 mode->c_iflag &= ~ISTRIP;
1151 mode->c_oflag &= ~OPOST;
1153 } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
1154 if ((info == &mode_info[IDX_raw] && reversed)
1155 || (info == &mode_info[IDX_cooked] && !reversed)
1158 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1159 mode->c_oflag |= OPOST;
1160 mode->c_lflag |= ISIG | ICANON;
1162 mode->c_cc[VEOF] = CEOF;
1165 mode->c_cc[VEOL] = CEOL;
1170 mode->c_oflag &= ~OPOST;
1171 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1172 mode->c_cc[VMIN] = 1;
1173 mode->c_cc[VTIME] = 0;
1177 else if (info == &mode_info[IDX_decctlq]) {
1179 mode->c_iflag |= IXANY;
1181 mode->c_iflag &= ~IXANY;
1185 else if (info == &mode_info[IDX_tabs]) {
1187 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1189 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1193 else if (info == &mode_info[IDX_tabs]) {
1195 mode->c_oflag |= OXTABS;
1197 mode->c_oflag &= ~OXTABS;
1200 #if XCASE && IUCLC && OLCUC
1201 else if (info==&mode_info[IDX_lcase] || info==&mode_info[IDX_LCASE]) {
1203 mode->c_lflag &= ~XCASE;
1204 mode->c_iflag &= ~IUCLC;
1205 mode->c_oflag &= ~OLCUC;
1207 mode->c_lflag |= XCASE;
1208 mode->c_iflag |= IUCLC;
1209 mode->c_oflag |= OLCUC;
1213 else if (info == &mode_info[IDX_crt]) {
1214 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1215 } else if (info == &mode_info[IDX_dec]) {
1216 mode->c_cc[VINTR] = 3; /* ^C */
1217 mode->c_cc[VERASE] = 127; /* DEL */
1218 mode->c_cc[VKILL] = 21; /* ^U */
1219 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1220 if (IXANY) mode->c_iflag &= ~IXANY;
1224 static void set_control_char_or_die(const struct control_info *info,
1225 const char *arg, struct termios *mode)
1227 unsigned char value;
1229 if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
1230 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1231 else if (arg[0] == '\0' || arg[1] == '\0')
1233 else if (strcmp(arg, "^-") == 0 || strcmp(arg, "undef") == 0)
1234 value = _POSIX_VDISABLE;
1235 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1236 value = arg[1] & 0x1f; /* Non-letters get weird results */
1240 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1241 mode->c_cc[info->offset] = value;
1244 #define STTY_require_set_attr (1 << 0)
1245 #define STTY_speed_was_set (1 << 1)
1246 #define STTY_verbose_output (1 << 2)
1247 #define STTY_recoverable_output (1 << 3)
1248 #define STTY_noargs (1 << 4)
1250 int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1251 int stty_main(int argc UNUSED_PARAM, char **argv)
1253 struct termios mode;
1254 void (*output_func)(const struct termios *, int);
1255 const char *file_name = NULL;
1256 int display_all = 0;
1262 stty_state = STTY_noargs;
1263 output_func = do_display;
1265 /* First pass: only parse/verify command line params */
1268 const struct mode_info *mp;
1269 const struct control_info *cp;
1270 const char *arg = argv[k];
1271 const char *argnext = argv[k+1];
1274 if (arg[0] == '-') {
1276 mp = find_mode(arg+1);
1278 if (!(mp->flags & REV))
1279 goto invalid_argument;
1280 stty_state &= ~STTY_noargs;
1283 /* It is an option - parse it */
1288 stty_state |= STTY_verbose_output;
1289 output_func = do_display;
1293 stty_state |= STTY_recoverable_output;
1294 output_func = display_recoverable;
1298 bb_error_msg_and_die("only one device may be specified");
1299 file_name = &arg[i+1]; /* "-Fdevice" ? */
1300 if (!file_name[0]) { /* nope, "-F device" */
1301 int p = k+1; /* argv[p] is argnext */
1302 file_name = argnext;
1304 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1305 /* remove -F param from arg[vc] */
1307 argv[p] = argv[p+1];
1313 goto invalid_argument;
1320 mp = find_mode(arg);
1322 stty_state &= ~STTY_noargs;
1326 cp = find_control(arg);
1329 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1330 /* called for the side effect of xfunc death only */
1331 set_control_char_or_die(cp, argnext, &mode);
1332 stty_state &= ~STTY_noargs;
1337 param = find_param(arg);
1338 if (param & param_need_arg) {
1340 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1348 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1350 # endif /* else fall-through */
1356 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1363 /* called for the side effect of xfunc death only */
1364 set_speed_or_die(input_speed, argnext, &mode);
1367 /* called for the side effect of xfunc death only */
1368 set_speed_or_die(output_speed, argnext, &mode);
1371 if (recover_mode(arg, &mode) == 1) break;
1372 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1374 bb_error_msg_and_die("invalid argument '%s'", arg);
1376 stty_state &= ~STTY_noargs;
1379 /* Specifying both -a and -g is an error */
1380 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1381 (STTY_verbose_output | STTY_recoverable_output)
1383 bb_error_msg_and_die("-a and -g are mutually exclusive");
1385 /* Specifying -a or -g with non-options is an error */
1386 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output))
1387 && !(stty_state & STTY_noargs)
1389 bb_error_msg_and_die("modes may not be set when -a or -g is used");
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 * I think the below chunk is not necessary on Linux.
1513 * If you are deleting it, also delete STTY_speed_was_set bit -
1514 * it is only ever checked here.
1516 #if 0 /* was "if CIBAUD" */
1517 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1518 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1519 sometimes (m1 != m2). The only difference is in the four bits
1520 of the c_cflag field corresponding to the baud rate. To save
1521 Sun users a little confusion, don't report an error if this
1522 happens. But suppress the error only if we haven't tried to
1523 set the baud rate explicitly -- otherwise we'd never give an
1524 error for a true failure to set the baud rate */
1526 new_mode.c_cflag &= (~CIBAUD);
1527 if ((stty_state & STTY_speed_was_set)
1528 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1530 perror_on_device_and_die("%s: cannot perform all requested operations");
1534 return EXIT_SUCCESS;