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; \
785 /* Return a string that is the printable representation of character CH */
786 /* Adapted from 'cat' by Torbjorn Granlund */
787 static const char *visible(unsigned ch)
791 if (ch == _POSIX_VDISABLE)
803 } else if (ch < 127) {
814 static void set_speed_or_die(enum speed_setting type, const char *arg,
815 struct termios *mode)
819 baud = tty_value_to_baud(xatou(arg));
821 if (type != output_speed) { /* either input or both */
822 cfsetispeed(mode, baud);
824 if (type != input_speed) { /* either output or both */
825 cfsetospeed(mode, baud);
829 static NORETURN void perror_on_device_and_die(const char *fmt)
831 bb_perror_msg_and_die(fmt, G.device_name);
834 static void perror_on_device(const char *fmt)
836 bb_perror_msg(fmt, G.device_name);
839 /* Print format string MESSAGE and optional args.
840 Wrap to next line first if it won't fit.
841 Print a space first unless MESSAGE will start a new line */
842 static void wrapf(const char *message, ...)
848 va_start(args, message);
849 buflen = vsnprintf(buf, sizeof(buf), message, args);
851 /* We seem to be called only with suitable lengths, but check if
852 somebody failed to adhere to this assumption just to be sure. */
853 if (!buflen || buflen >= sizeof(buf)) return;
855 if (G.current_col > 0) {
857 if (buf[0] != '\n') {
858 if (G.current_col + buflen >= G.max_col) {
866 G.current_col += buflen;
867 if (buf[buflen-1] == '\n')
871 static void newline(void)
873 if (G.current_col != 0)
878 static void set_window_size(int rows, int cols)
880 struct winsize win = { 0, 0, 0, 0 };
882 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
883 if (errno != EINVAL) {
886 memset(&win, 0, sizeof(win));
894 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
896 perror_on_device("%s");
900 static void display_window_size(int fancy)
902 const char *fmt_str = "%s\0%s: no size information for this device";
903 unsigned width, height;
905 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
906 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
907 perror_on_device(fmt_str);
910 wrapf(fancy ? "rows %u; columns %u;" : "%u %u\n",
915 static const struct suffix_mult stty_suffixes[] = {
922 static const struct mode_info *find_mode(const char *name)
924 int i = index_in_strings(mode_name, name);
925 return i >= 0 ? &mode_info[i] : NULL;
928 static const struct control_info *find_control(const char *name)
930 int i = index_in_strings(control_name, name);
931 return i >= 0 ? &control_info[i] : NULL;
935 param_need_arg = 0x80,
936 param_line = 1 | 0x80,
937 param_rows = 2 | 0x80,
938 param_cols = 3 | 0x80,
939 param_columns = 4 | 0x80,
942 param_ispeed = 7 | 0x80,
943 param_ospeed = 8 | 0x80,
946 static int find_param(const char *name)
948 static const char params[] ALIGN1 =
957 int i = index_in_strings(params, name) + 1;
960 if (i != 5 && i != 6)
965 static int recover_mode(const char *arg, struct termios *mode)
969 unsigned long iflag, oflag, cflag, lflag;
971 /* Scan into temporaries since it is too much trouble to figure out
972 the right format for 'tcflag_t' */
973 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
974 &iflag, &oflag, &cflag, &lflag, &n) != 4)
976 mode->c_iflag = iflag;
977 mode->c_oflag = oflag;
978 mode->c_cflag = cflag;
979 mode->c_lflag = lflag;
981 for (i = 0; i < NCCS; ++i) {
982 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
988 /* Fail if there are too many fields */
995 static void display_recoverable(const struct termios *mode,
996 int UNUSED_PARAM dummy)
999 printf("%lx:%lx:%lx:%lx",
1000 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1001 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1002 for (i = 0; i < NCCS; ++i)
1003 printf(":%x", (unsigned int) mode->c_cc[i]);
1007 static void display_speed(const struct termios *mode, int fancy)
1009 //____________________ 01234567 8 9
1010 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
1011 unsigned long ispeed, ospeed;
1013 ispeed = cfgetispeed(mode);
1014 ospeed = cfgetospeed(mode);
1015 if (ispeed == 0 || ispeed == ospeed) {
1016 ispeed = ospeed; /* in case ispeed was 0 */
1017 //________ 0123 4 5 6 7 8 9
1018 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
1020 if (fancy) fmt_str += 9;
1021 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
1024 static void do_display(const struct termios *mode, int all)
1029 int prev_type = control;
1031 display_speed(mode, 1);
1033 display_window_size(1);
1035 wrapf("line = %u;\n", mode->c_line);
1040 for (i = 0; i != CIDX_min; ++i) {
1041 /* If swtch is the same as susp, don't print both */
1043 if (i == CIDX_swtch)
1046 /* If eof uses the same slot as min, only print whichever applies */
1048 if (!(mode->c_lflag & ICANON)
1049 && (i == CIDX_eof || i == CIDX_eol)
1054 wrapf("%s = %s;", nth_string(control_name, i),
1055 visible(mode->c_cc[control_info[i].offset]));
1058 if ((mode->c_lflag & ICANON) == 0)
1060 wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1063 for (i = 0; i < NUM_mode_info; ++i) {
1064 if (mode_info[i].flags & OMIT)
1066 if (mode_info[i].type != prev_type) {
1068 prev_type = mode_info[i].type;
1071 bitsp = get_ptr_to_tcflag(mode_info[i].type, mode);
1072 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1073 if ((*bitsp & mask) == mode_info[i].bits) {
1074 if (all || (mode_info[i].flags & SANE_UNSET))
1075 wrapf("-%s"+1, nth_string(mode_name, i));
1077 if ((all && mode_info[i].flags & REV)
1078 || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
1080 wrapf("-%s", nth_string(mode_name, i));
1087 static void sane_mode(struct termios *mode)
1091 for (i = 0; i < NUM_control_info; ++i) {
1096 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1099 for (i = 0; i < NUM_mode_info; ++i) {
1101 tcflag_t *bitsp = get_ptr_to_tcflag(mode_info[i].type, mode);
1105 val = *bitsp & ~((unsigned long)mode_info[i].mask);
1106 if (mode_info[i].flags & SANE_SET) {
1107 *bitsp = val | mode_info[i].bits;
1109 if (mode_info[i].flags & SANE_UNSET) {
1110 *bitsp = val & ~mode_info[i].bits;
1115 static void set_mode(const struct mode_info *info, int reversed,
1116 struct termios *mode)
1120 bitsp = get_ptr_to_tcflag(info->type, mode);
1123 tcflag_t val = *bitsp & ~info->mask;
1125 *bitsp = val & ~info->bits;
1127 *bitsp = val | info->bits;
1131 /* !bitsp - it's a "combination" mode */
1132 if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
1134 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1136 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
1137 } else if (info == &mode_info[IDX_oddp]) {
1139 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1141 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
1142 } else if (info == &mode_info[IDX_nl]) {
1144 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
1145 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
1147 mode->c_iflag = mode->c_iflag & ~ICRNL;
1148 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1150 } else if (info == &mode_info[IDX_ek]) {
1151 mode->c_cc[VERASE] = CERASE;
1152 mode->c_cc[VKILL] = CKILL;
1153 } else if (info == &mode_info[IDX_sane]) {
1155 } else if (info == &mode_info[IDX_cbreak]) {
1157 mode->c_lflag |= ICANON;
1159 mode->c_lflag &= ~ICANON;
1160 } else if (info == &mode_info[IDX_pass8]) {
1162 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1163 mode->c_iflag |= ISTRIP;
1165 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1166 mode->c_iflag &= ~ISTRIP;
1168 } else if (info == &mode_info[IDX_litout]) {
1170 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1171 mode->c_iflag |= ISTRIP;
1172 mode->c_oflag |= OPOST;
1174 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1175 mode->c_iflag &= ~ISTRIP;
1176 mode->c_oflag &= ~OPOST;
1178 } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
1179 if ((info == &mode_info[IDX_raw] && reversed)
1180 || (info == &mode_info[IDX_cooked] && !reversed)
1183 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1184 mode->c_oflag |= OPOST;
1185 mode->c_lflag |= ISIG | ICANON;
1187 mode->c_cc[VEOF] = CEOF;
1190 mode->c_cc[VEOL] = CEOL;
1195 mode->c_oflag &= ~OPOST;
1196 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1197 mode->c_cc[VMIN] = 1;
1198 mode->c_cc[VTIME] = 0;
1202 else if (info == &mode_info[IDX_decctlq]) {
1204 mode->c_iflag |= IXANY;
1206 mode->c_iflag &= ~IXANY;
1210 else if (info == &mode_info[IDX_tabs]) {
1212 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1214 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1218 else if (info == &mode_info[IDX_tabs]) {
1220 mode->c_oflag |= OXTABS;
1222 mode->c_oflag &= ~OXTABS;
1225 #if XCASE && IUCLC && OLCUC
1226 else if (info==&mode_info[IDX_lcase] || info==&mode_info[IDX_LCASE]) {
1228 mode->c_lflag &= ~XCASE;
1229 mode->c_iflag &= ~IUCLC;
1230 mode->c_oflag &= ~OLCUC;
1232 mode->c_lflag |= XCASE;
1233 mode->c_iflag |= IUCLC;
1234 mode->c_oflag |= OLCUC;
1238 else if (info == &mode_info[IDX_crt]) {
1239 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1240 } else if (info == &mode_info[IDX_dec]) {
1241 mode->c_cc[VINTR] = 3; /* ^C */
1242 mode->c_cc[VERASE] = 127; /* DEL */
1243 mode->c_cc[VKILL] = 21; /* ^U */
1244 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1245 if (IXANY) mode->c_iflag &= ~IXANY;
1249 static void set_control_char_or_die(const struct control_info *info,
1250 const char *arg, struct termios *mode)
1252 unsigned char value;
1254 if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
1255 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1256 else if (arg[0] == '\0' || arg[1] == '\0')
1258 else if (strcmp(arg, "^-") == 0 || strcmp(arg, "undef") == 0)
1259 value = _POSIX_VDISABLE;
1260 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1261 value = arg[1] & 0x1f; /* Non-letters get weird results */
1265 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1266 mode->c_cc[info->offset] = value;
1269 #define STTY_require_set_attr (1 << 0)
1270 #define STTY_speed_was_set (1 << 1)
1271 #define STTY_verbose_output (1 << 2)
1272 #define STTY_recoverable_output (1 << 3)
1273 #define STTY_noargs (1 << 4)
1275 int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1276 int stty_main(int argc UNUSED_PARAM, char **argv)
1278 struct termios mode;
1279 void (*output_func)(const struct termios *, int);
1280 const char *file_name = NULL;
1281 int display_all = 0;
1287 stty_state = STTY_noargs;
1288 output_func = do_display;
1290 /* First pass: only parse/verify command line params */
1293 const struct mode_info *mp;
1294 const struct control_info *cp;
1295 const char *arg = argv[k];
1296 const char *argnext = argv[k+1];
1299 if (arg[0] == '-') {
1301 mp = find_mode(arg+1);
1303 if (!(mp->flags & REV))
1304 goto invalid_argument;
1305 stty_state &= ~STTY_noargs;
1308 /* It is an option - parse it */
1313 stty_state |= STTY_verbose_output;
1314 output_func = do_display;
1318 stty_state |= STTY_recoverable_output;
1319 output_func = display_recoverable;
1323 bb_error_msg_and_die("only one device may be specified");
1324 file_name = &arg[i+1]; /* "-Fdevice" ? */
1325 if (!file_name[0]) { /* nope, "-F device" */
1326 int p = k+1; /* argv[p] is argnext */
1327 file_name = argnext;
1329 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1330 /* remove -F param from arg[vc] */
1332 argv[p] = argv[p+1];
1338 goto invalid_argument;
1345 mp = find_mode(arg);
1347 stty_state &= ~STTY_noargs;
1351 cp = find_control(arg);
1354 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1355 /* called for the side effect of xfunc death only */
1356 set_control_char_or_die(cp, argnext, &mode);
1357 stty_state &= ~STTY_noargs;
1362 param = find_param(arg);
1363 if (param & param_need_arg) {
1365 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1373 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1375 # endif /* else fall-through */
1381 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1388 /* called for the side effect of xfunc death only */
1389 set_speed_or_die(input_speed, argnext, &mode);
1392 /* called for the side effect of xfunc death only */
1393 set_speed_or_die(output_speed, argnext, &mode);
1396 if (recover_mode(arg, &mode) == 1) break;
1397 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1399 bb_error_msg_and_die("invalid argument '%s'", arg);
1401 stty_state &= ~STTY_noargs;
1404 /* Specifying both -a and -g is an error */
1405 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1406 (STTY_verbose_output | STTY_recoverable_output)
1408 bb_error_msg_and_die("-a and -g are mutually exclusive");
1410 /* Specifying -a or -g with non-options is an error */
1411 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output))
1412 && !(stty_state & STTY_noargs)
1414 bb_error_msg_and_die("modes may not be set when -a or -g is used");
1417 /* Now it is safe to start doing things */
1419 G.device_name = file_name;
1420 xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO);
1421 ndelay_off(STDIN_FILENO);
1424 /* Initialize to all zeroes so there is no risk memcmp will report a
1425 spurious difference in an uninitialized portion of the structure */
1426 memset(&mode, 0, sizeof(mode));
1427 if (tcgetattr(STDIN_FILENO, &mode))
1428 perror_on_device_and_die("%s");
1430 if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
1431 get_terminal_width_height(STDOUT_FILENO, &G.max_col, NULL);
1432 output_func(&mode, display_all);
1433 return EXIT_SUCCESS;
1436 /* Second pass: perform actions */
1439 const struct mode_info *mp;
1440 const struct control_info *cp;
1441 const char *arg = argv[k];
1442 const char *argnext = argv[k+1];
1445 if (arg[0] == '-') {
1446 mp = find_mode(arg+1);
1448 set_mode(mp, 1 /* reversed */, &mode);
1449 stty_state |= STTY_require_set_attr;
1451 /* It is an option - already parsed. Skip it */
1455 mp = find_mode(arg);
1457 set_mode(mp, 0 /* non-reversed */, &mode);
1458 stty_state |= STTY_require_set_attr;
1462 cp = find_control(arg);
1465 set_control_char_or_die(cp, argnext, &mode);
1466 stty_state |= STTY_require_set_attr;
1470 param = find_param(arg);
1471 if (param & param_need_arg) {
1478 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1479 stty_state |= STTY_require_set_attr;
1485 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1488 display_window_size(0);
1491 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1495 display_speed(&mode, 0);
1498 set_speed_or_die(input_speed, argnext, &mode);
1499 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1502 set_speed_or_die(output_speed, argnext, &mode);
1503 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1506 if (recover_mode(arg, &mode) == 1)
1507 stty_state |= STTY_require_set_attr;
1508 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
1509 set_speed_or_die(both_speeds, arg, &mode);
1510 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1511 } /* else - impossible (caught in the first pass):
1512 bb_error_msg_and_die("invalid argument '%s'", arg); */
1516 if (stty_state & STTY_require_set_attr) {
1517 struct termios new_mode;
1519 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1520 perror_on_device_and_die("%s");
1522 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1523 it performs *any* of the requested operations. This means it
1524 can report 'success' when it has actually failed to perform
1525 some proper subset of the requested operations. To detect
1526 this partial failure, get the current terminal attributes and
1527 compare them to the requested ones */
1529 /* Initialize to all zeroes so there is no risk memcmp will report a
1530 spurious difference in an uninitialized portion of the structure */
1531 memset(&new_mode, 0, sizeof(new_mode));
1532 if (tcgetattr(STDIN_FILENO, &new_mode))
1533 perror_on_device_and_die("%s");
1535 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1537 * I think the below chunk is not necessary on Linux.
1538 * If you are deleting it, also delete STTY_speed_was_set bit -
1539 * it is only ever checked here.
1541 #if 0 /* was "if CIBAUD" */
1542 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1543 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1544 sometimes (m1 != m2). The only difference is in the four bits
1545 of the c_cflag field corresponding to the baud rate. To save
1546 Sun users a little confusion, don't report an error if this
1547 happens. But suppress the error only if we haven't tried to
1548 set the baud rate explicitly -- otherwise we'd never give an
1549 error for a true failure to set the baud rate */
1551 new_mode.c_cflag &= (~CIBAUD);
1552 if ((stty_state & STTY_speed_was_set)
1553 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1555 perror_on_device_and_die("%s: cannot perform all requested operations");
1559 return EXIT_SUCCESS;