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 the GPL v2 or later, see the file LICENSE in this tarball.
7 /* Usage: stty [-ag] [-F device] [setting...]
10 -a Write all current settings to stdout in human-readable form.
11 -g Write all current settings to stdout in stty-readable form.
12 -F Open and use the specified device instead of stdin
14 If no args are given, write to stdout the baud rate and settings that
15 have been changed from their defaults. Mode reading and changes
16 are done on the specified device, or stdin if none was specified.
18 David MacKenzie <djm@gnu.ai.mit.edu>
20 Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
26 #ifndef _POSIX_VDISABLE
27 # define _POSIX_VDISABLE ((unsigned char) 0)
30 #define Control(c) ((c) & 0x1f)
31 /* Canonical values for control characters */
33 # define CINTR Control('c')
42 # define CKILL Control('u')
45 # define CEOF Control('d')
48 # define CEOL _POSIX_VDISABLE
51 # define CSTART Control('q')
54 # define CSTOP Control('s')
57 # define CSUSP Control('z')
59 #if defined(VEOL2) && !defined(CEOL2)
60 # define CEOL2 _POSIX_VDISABLE
62 /* ISC renamed swtch to susp for termios, but we'll accept either name */
63 #if defined(VSUSP) && !defined(VSWTCH)
67 #if defined(VSWTCH) && !defined(CSWTCH)
68 # define CSWTCH _POSIX_VDISABLE
71 /* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'.
72 So the default is to disable 'swtch.' */
73 #if defined(__sparc__) && defined(__svr4__)
75 # define CSWTCH _POSIX_VDISABLE
78 #if defined(VWERSE) && !defined(VWERASE) /* AIX-3.2.5 */
79 # define VWERASE VWERSE
81 #if defined(VDSUSP) && !defined(CDSUSP)
82 # define CDSUSP Control('y')
84 #if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */
85 # define VREPRINT VRPRNT
87 #if defined(VREPRINT) && !defined(CRPRNT)
88 # define CRPRNT Control('r')
90 #if defined(VWERASE) && !defined(CWERASE)
91 # define CWERASE Control('w')
93 #if defined(VLNEXT) && !defined(CLNEXT)
94 # define CLNEXT Control('v')
96 #if defined(VDISCARD) && !defined(VFLUSHO)
97 # define VFLUSHO VDISCARD
99 #if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */
100 # define VFLUSHO VFLUSH
102 #if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */
103 # define ECHOCTL CTLECH
105 #if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */
106 # define ECHOCTL TCTLECH
108 #if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */
109 # define ECHOKE CRTKIL
111 #if defined(VFLUSHO) && !defined(CFLUSHO)
112 # define CFLUSHO Control('o')
114 #if defined(VSTATUS) && !defined(CSTATUS)
115 # define CSTATUS Control('t')
118 /* Which speeds to set */
120 input_speed, output_speed, both_speeds
123 /* Which member(s) of 'struct termios' a mode uses */
125 /* Do NOT change the order or values, as mode_type_flag()
127 control, input, output, local, combination
130 /* Flags for 'struct mode_info' */
131 #define SANE_SET 1 /* Set in 'sane' mode */
132 #define SANE_UNSET 2 /* Unset in 'sane' mode */
133 #define REV 4 /* Can be turned off by prepending '-' */
134 #define OMIT 8 /* Don't display value */
138 * This structure should be kept as small as humanly possible.
141 const uint8_t type; /* Which structure element to change */
142 const uint8_t flags; /* Setting and display options */
143 /* only these values are ever used, so... */
144 #if (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100
146 #elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000
149 const tcflag_t mask; /* Other bits to turn off for this mode */
151 /* was using short here, but ppc32 was unhappy */
152 const tcflag_t bits; /* Bits to set for this mode */
156 /* Must match mode_name[] and mode_info[] order! */
173 #if defined(TABDLY) || defined(OXTABS)
176 #if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
182 #define MI_ENTRY(N,T,F,B,M) N "\0"
184 /* Mode names given on command line */
185 static const char mode_name[] =
186 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
187 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
188 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
189 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
190 MI_ENTRY("ek", combination, OMIT, 0, 0 )
191 MI_ENTRY("sane", combination, OMIT, 0, 0 )
192 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
193 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
194 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
195 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
196 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
197 MI_ENTRY("crt", combination, OMIT, 0, 0 )
198 MI_ENTRY("dec", combination, OMIT, 0, 0 )
200 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
202 #if defined(TABDLY) || defined(OXTABS)
203 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
205 #if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
206 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 )
207 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 )
209 MI_ENTRY("parenb", control, REV, PARENB, 0 )
210 MI_ENTRY("parodd", control, REV, PARODD, 0 )
211 MI_ENTRY("cs5", control, 0, CS5, CSIZE)
212 MI_ENTRY("cs6", control, 0, CS6, CSIZE)
213 MI_ENTRY("cs7", control, 0, CS7, CSIZE)
214 MI_ENTRY("cs8", control, 0, CS8, CSIZE)
215 MI_ENTRY("hupcl", control, REV, HUPCL, 0 )
216 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 )
217 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 )
218 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 )
219 MI_ENTRY("clocal", control, REV, CLOCAL, 0 )
221 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
223 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 )
224 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 )
225 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 )
226 MI_ENTRY("parmrk", input, REV, PARMRK, 0 )
227 MI_ENTRY("inpck", input, REV, INPCK, 0 )
228 MI_ENTRY("istrip", input, REV, ISTRIP, 0 )
229 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 )
230 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 )
231 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
232 MI_ENTRY("ixon", input, REV, IXON, 0 )
233 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
234 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 )
236 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
239 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
242 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
244 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
246 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
249 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
252 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
255 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
258 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
261 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
264 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
267 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
268 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
271 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY)
272 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY)
273 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY)
274 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY)
278 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY)
279 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY)
280 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY)
281 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY)
284 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
289 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
290 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
293 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
294 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
297 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
298 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
300 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
301 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
303 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
305 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
306 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
307 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 )
308 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
309 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
310 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
312 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
315 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
318 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
319 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 )
322 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
323 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 )
326 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
327 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 )
332 #define MI_ENTRY(N,T,F,B,M) { T, F, M, B },
334 static const struct mode_info mode_info[] = {
335 /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */
336 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
337 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
338 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
339 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
340 MI_ENTRY("ek", combination, OMIT, 0, 0 )
341 MI_ENTRY("sane", combination, OMIT, 0, 0 )
342 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
343 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
344 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
345 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
346 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
347 MI_ENTRY("crt", combination, OMIT, 0, 0 )
348 MI_ENTRY("dec", combination, OMIT, 0, 0 )
350 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
352 #if defined(TABDLY) || defined(OXTABS)
353 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
355 #if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
356 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 )
357 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 )
359 MI_ENTRY("parenb", control, REV, PARENB, 0 )
360 MI_ENTRY("parodd", control, REV, PARODD, 0 )
361 MI_ENTRY("cs5", control, 0, CS5, CSIZE)
362 MI_ENTRY("cs6", control, 0, CS6, CSIZE)
363 MI_ENTRY("cs7", control, 0, CS7, CSIZE)
364 MI_ENTRY("cs8", control, 0, CS8, CSIZE)
365 MI_ENTRY("hupcl", control, REV, HUPCL, 0 )
366 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 )
367 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 )
368 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 )
369 MI_ENTRY("clocal", control, REV, CLOCAL, 0 )
371 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
373 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 )
374 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 )
375 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 )
376 MI_ENTRY("parmrk", input, REV, PARMRK, 0 )
377 MI_ENTRY("inpck", input, REV, INPCK, 0 )
378 MI_ENTRY("istrip", input, REV, ISTRIP, 0 )
379 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 )
380 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 )
381 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
382 MI_ENTRY("ixon", input, REV, IXON, 0 )
383 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
384 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 )
386 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
389 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
392 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
394 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
396 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
399 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
402 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
405 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
408 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
411 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
414 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
417 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
418 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
421 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY)
422 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY)
423 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY)
424 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY)
428 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY)
429 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY)
430 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY)
431 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY)
434 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
439 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
440 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
443 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
444 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
447 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
448 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
450 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
451 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
453 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
455 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
456 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
457 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 )
458 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
459 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
460 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
462 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
465 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
468 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
469 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 )
472 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
473 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 )
476 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
477 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 )
482 NUM_mode_info = ARRAY_SIZE(mode_info)
486 /* Control characters */
487 struct control_info {
488 const uint8_t saneval; /* Value to set for 'stty sane' */
489 const uint8_t offset; /* Offset in c_cc */
493 /* Must match control_name[] and control_info[] order! */
531 #define CI_ENTRY(n,s,o) n "\0"
533 /* Name given on command line */
534 static const char control_name[] =
535 CI_ENTRY("intr", CINTR, VINTR )
536 CI_ENTRY("quit", CQUIT, VQUIT )
537 CI_ENTRY("erase", CERASE, VERASE )
538 CI_ENTRY("kill", CKILL, VKILL )
539 CI_ENTRY("eof", CEOF, VEOF )
540 CI_ENTRY("eol", CEOL, VEOL )
542 CI_ENTRY("eol2", CEOL2, VEOL2 )
545 CI_ENTRY("swtch", CSWTCH, VSWTCH )
547 CI_ENTRY("start", CSTART, VSTART )
548 CI_ENTRY("stop", CSTOP, VSTOP )
549 CI_ENTRY("susp", CSUSP, VSUSP )
551 CI_ENTRY("dsusp", CDSUSP, VDSUSP )
554 CI_ENTRY("rprnt", CRPRNT, VREPRINT)
557 CI_ENTRY("werase", CWERASE, VWERASE )
560 CI_ENTRY("lnext", CLNEXT, VLNEXT )
563 CI_ENTRY("flush", CFLUSHO, VFLUSHO )
566 CI_ENTRY("status", CSTATUS, VSTATUS )
568 /* These must be last because of the display routines */
569 CI_ENTRY("min", 1, VMIN )
570 CI_ENTRY("time", 0, VTIME )
574 #define CI_ENTRY(n,s,o) { s, o },
576 static const struct control_info control_info[] = {
577 /* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */
578 CI_ENTRY("intr", CINTR, VINTR )
579 CI_ENTRY("quit", CQUIT, VQUIT )
580 CI_ENTRY("erase", CERASE, VERASE )
581 CI_ENTRY("kill", CKILL, VKILL )
582 CI_ENTRY("eof", CEOF, VEOF )
583 CI_ENTRY("eol", CEOL, VEOL )
585 CI_ENTRY("eol2", CEOL2, VEOL2 )
588 CI_ENTRY("swtch", CSWTCH, VSWTCH )
590 CI_ENTRY("start", CSTART, VSTART )
591 CI_ENTRY("stop", CSTOP, VSTOP )
592 CI_ENTRY("susp", CSUSP, VSUSP )
594 CI_ENTRY("dsusp", CDSUSP, VDSUSP )
597 CI_ENTRY("rprnt", CRPRNT, VREPRINT)
600 CI_ENTRY("werase", CWERASE, VWERASE )
603 CI_ENTRY("lnext", CLNEXT, VLNEXT )
606 CI_ENTRY("flush", CFLUSHO, VFLUSHO )
609 CI_ENTRY("status", CSTATUS, VSTATUS )
611 /* These must be last because of the display routines */
612 CI_ENTRY("min", 1, VMIN )
613 CI_ENTRY("time", 0, VTIME )
617 NUM_control_info = ARRAY_SIZE(control_info)
622 const char *device_name; // = bb_msg_standard_input;
623 /* The width of the screen, for output wrapping */
624 unsigned max_col; // = 80;
625 /* Current position, to know when to wrap */
626 unsigned current_col;
629 #define G (*(struct globals*)&bb_common_bufsiz1)
630 #define INIT_G() do { \
631 G.device_name = bb_msg_standard_input; \
636 /* Return a string that is the printable representation of character CH */
637 /* Adapted from 'cat' by Torbjorn Granlund */
638 static const char *visible(unsigned ch)
642 if (ch == _POSIX_VDISABLE)
654 } else if (ch < 127) {
665 static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
667 static const uint8_t tcflag_offsets[] ALIGN1 = {
668 offsetof(struct termios, c_cflag), /* control */
669 offsetof(struct termios, c_iflag), /* input */
670 offsetof(struct termios, c_oflag), /* output */
671 offsetof(struct termios, c_lflag) /* local */
675 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
680 static void set_speed_or_die(enum speed_setting type, const char *arg,
681 struct termios *mode)
685 baud = tty_value_to_baud(xatou(arg));
687 if (type != output_speed) { /* either input or both */
688 cfsetispeed(mode, baud);
690 if (type != input_speed) { /* either output or both */
691 cfsetospeed(mode, baud);
695 static NORETURN void perror_on_device_and_die(const char *fmt)
697 bb_perror_msg_and_die(fmt, G.device_name);
700 static void perror_on_device(const char *fmt)
702 bb_perror_msg(fmt, G.device_name);
705 /* Print format string MESSAGE and optional args.
706 Wrap to next line first if it won't fit.
707 Print a space first unless MESSAGE will start a new line */
708 static void wrapf(const char *message, ...)
714 va_start(args, message);
715 buflen = vsnprintf(buf, sizeof(buf), message, args);
717 /* We seem to be called only with suitable lengths, but check if
718 somebody failed to adhere to this assumption just to be sure. */
719 if (!buflen || buflen >= sizeof(buf)) return;
721 if (G.current_col > 0) {
723 if (buf[0] != '\n') {
724 if (G.current_col + buflen >= G.max_col) {
732 G.current_col += buflen;
733 if (buf[buflen-1] == '\n')
737 static void set_window_size(const int rows, const int cols)
739 struct winsize win = { 0, 0, 0, 0 };
741 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
742 if (errno != EINVAL) {
745 memset(&win, 0, sizeof(win));
753 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
755 perror_on_device("%s");
758 static void display_window_size(const int fancy)
760 const char *fmt_str = "%s\0%s: no size information for this device";
761 unsigned width, height;
763 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
764 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
765 perror_on_device(fmt_str);
768 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
773 static const struct suffix_mult stty_suffixes[] = {
780 static const struct mode_info *find_mode(const char *name)
782 int i = index_in_strings(mode_name, name);
783 return i >= 0 ? &mode_info[i] : NULL;
786 static const struct control_info *find_control(const char *name)
788 int i = index_in_strings(control_name, name);
789 return i >= 0 ? &control_info[i] : NULL;
793 param_need_arg = 0x80,
794 param_line = 1 | 0x80,
795 param_rows = 2 | 0x80,
796 param_cols = 3 | 0x80,
797 param_columns = 4 | 0x80,
800 param_ispeed = 7 | 0x80,
801 param_ospeed = 8 | 0x80,
804 static int find_param(const char *name)
806 static const char params[] ALIGN1 =
815 int i = index_in_strings(params, name) + 1;
818 if (i != 5 && i != 6)
823 static int recover_mode(const char *arg, struct termios *mode)
827 unsigned long iflag, oflag, cflag, lflag;
829 /* Scan into temporaries since it is too much trouble to figure out
830 the right format for 'tcflag_t' */
831 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
832 &iflag, &oflag, &cflag, &lflag, &n) != 4)
834 mode->c_iflag = iflag;
835 mode->c_oflag = oflag;
836 mode->c_cflag = cflag;
837 mode->c_lflag = lflag;
839 for (i = 0; i < NCCS; ++i) {
840 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
846 /* Fail if there are too many fields */
853 static void display_recoverable(const struct termios *mode,
854 int UNUSED_PARAM dummy)
857 printf("%lx:%lx:%lx:%lx",
858 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
859 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
860 for (i = 0; i < NCCS; ++i)
861 printf(":%x", (unsigned int) mode->c_cc[i]);
865 static void display_speed(const struct termios *mode, int fancy)
868 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
869 unsigned long ispeed, ospeed;
871 ospeed = ispeed = cfgetispeed(mode);
872 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
873 ispeed = ospeed; /* in case ispeed was 0 */
875 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
877 if (fancy) fmt_str += 9;
878 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
881 static void do_display(const struct termios *mode, const int all)
886 int prev_type = control;
888 display_speed(mode, 1);
890 display_window_size(1);
892 wrapf("line = %d;\n", mode->c_line);
897 for (i = 0; i != CIDX_min; ++i) {
898 /* If swtch is the same as susp, don't print both */
903 /* If eof uses the same slot as min, only print whichever applies */
905 if ((mode->c_lflag & ICANON) == 0
906 && (i == CIDX_eof || i == CIDX_eol)
911 wrapf("%s = %s;", nth_string(control_name, i),
912 visible(mode->c_cc[control_info[i].offset]));
915 if ((mode->c_lflag & ICANON) == 0)
917 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
918 if (G.current_col) wrapf("\n");
920 for (i = 0; i < NUM_mode_info; ++i) {
921 if (mode_info[i].flags & OMIT)
923 if (mode_info[i].type != prev_type) {
925 if (G.current_col) wrapf("\n");
926 prev_type = mode_info[i].type;
929 bitsp = mode_type_flag(mode_info[i].type, mode);
930 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
931 if ((*bitsp & mask) == mode_info[i].bits) {
932 if (all || (mode_info[i].flags & SANE_UNSET))
933 wrapf("-%s"+1, nth_string(mode_name, i));
935 if ((all && mode_info[i].flags & REV)
936 || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
938 wrapf("-%s", nth_string(mode_name, i));
942 if (G.current_col) wrapf("\n");
945 static void sane_mode(struct termios *mode)
950 for (i = 0; i < NUM_control_info; ++i) {
955 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
958 for (i = 0; i < NUM_mode_info; ++i) {
959 if (mode_info[i].flags & SANE_SET) {
960 bitsp = mode_type_flag(mode_info[i].type, mode);
961 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
963 } else if (mode_info[i].flags & SANE_UNSET) {
964 bitsp = mode_type_flag(mode_info[i].type, mode);
965 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
966 & ~mode_info[i].bits;
971 /* Save set_mode from #ifdef forest plague */
1006 static void set_mode(const struct mode_info *info, int reversed,
1007 struct termios *mode)
1011 bitsp = mode_type_flag(info->type, mode);
1015 *bitsp = *bitsp & ~info->mask & ~info->bits;
1017 *bitsp = (*bitsp & ~info->mask) | info->bits;
1021 /* Combination mode */
1022 if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
1024 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1026 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
1027 } else if (info == &mode_info[IDX_oddp]) {
1029 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1031 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
1032 } else if (info == &mode_info[IDX_nl]) {
1034 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
1035 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
1037 mode->c_iflag = mode->c_iflag & ~ICRNL;
1038 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1040 } else if (info == &mode_info[IDX_ek]) {
1041 mode->c_cc[VERASE] = CERASE;
1042 mode->c_cc[VKILL] = CKILL;
1043 } else if (info == &mode_info[IDX_sane]) {
1045 } else if (info == &mode_info[IDX_cbreak]) {
1047 mode->c_lflag |= ICANON;
1049 mode->c_lflag &= ~ICANON;
1050 } else if (info == &mode_info[IDX_pass8]) {
1052 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1053 mode->c_iflag |= ISTRIP;
1055 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1056 mode->c_iflag &= ~ISTRIP;
1058 } else if (info == &mode_info[IDX_litout]) {
1060 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1061 mode->c_iflag |= ISTRIP;
1062 mode->c_oflag |= OPOST;
1064 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1065 mode->c_iflag &= ~ISTRIP;
1066 mode->c_oflag &= ~OPOST;
1068 } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
1069 if ((info == &mode_info[IDX_raw] && reversed)
1070 || (info == &mode_info[IDX_cooked] && !reversed)
1073 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1074 mode->c_oflag |= OPOST;
1075 mode->c_lflag |= ISIG | ICANON;
1077 mode->c_cc[VEOF] = CEOF;
1080 mode->c_cc[VEOL] = CEOL;
1085 mode->c_oflag &= ~OPOST;
1086 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1087 mode->c_cc[VMIN] = 1;
1088 mode->c_cc[VTIME] = 0;
1091 else if (IXANY && info == &mode_info[IDX_decctlq]) {
1093 mode->c_iflag |= IXANY;
1095 mode->c_iflag &= ~IXANY;
1097 else if (TABDLY && info == &mode_info[IDX_tabs]) {
1099 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1101 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1103 else if (OXTABS && info == &mode_info[IDX_tabs]) {
1105 mode->c_oflag |= OXTABS;
1107 mode->c_oflag &= ~OXTABS;
1109 if (XCASE && IUCLC && OLCUC
1110 && (info == &mode_info[IDX_lcase] || info == &mode_info[IDX_LCASE])
1113 mode->c_lflag &= ~XCASE;
1114 mode->c_iflag &= ~IUCLC;
1115 mode->c_oflag &= ~OLCUC;
1117 mode->c_lflag |= XCASE;
1118 mode->c_iflag |= IUCLC;
1119 mode->c_oflag |= OLCUC;
1121 } else if (info == &mode_info[IDX_crt]) {
1122 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1123 } else if (info == &mode_info[IDX_dec]) {
1124 mode->c_cc[VINTR] = 3; /* ^C */
1125 mode->c_cc[VERASE] = 127; /* DEL */
1126 mode->c_cc[VKILL] = 21; /* ^U */
1127 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1128 if (IXANY) mode->c_iflag &= ~IXANY;
1132 static void set_control_char_or_die(const struct control_info *info,
1133 const char *arg, struct termios *mode)
1135 unsigned char value;
1137 if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
1138 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1139 else if (arg[0] == '\0' || arg[1] == '\0')
1141 else if (!strcmp(arg, "^-") || !strcmp(arg, "undef"))
1142 value = _POSIX_VDISABLE;
1143 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1144 value = arg[1] & 0x1f; /* Non-letters get weird results */
1148 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1149 mode->c_cc[info->offset] = value;
1152 #define STTY_require_set_attr (1 << 0)
1153 #define STTY_speed_was_set (1 << 1)
1154 #define STTY_verbose_output (1 << 2)
1155 #define STTY_recoverable_output (1 << 3)
1156 #define STTY_noargs (1 << 4)
1158 int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1159 int stty_main(int argc, char **argv)
1161 struct termios mode;
1162 void (*output_func)(const struct termios *, const int);
1163 const char *file_name = NULL;
1164 int display_all = 0;
1170 stty_state = STTY_noargs;
1171 output_func = do_display;
1173 /* First pass: only parse/verify command line params */
1176 const struct mode_info *mp;
1177 const struct control_info *cp;
1178 const char *arg = argv[k];
1179 const char *argnext = argv[k+1];
1182 if (arg[0] == '-') {
1184 mp = find_mode(arg+1);
1186 if (!(mp->flags & REV))
1187 goto invalid_argument;
1188 stty_state &= ~STTY_noargs;
1191 /* It is an option - parse it */
1196 stty_state |= STTY_verbose_output;
1197 output_func = do_display;
1201 stty_state |= STTY_recoverable_output;
1202 output_func = display_recoverable;
1206 bb_error_msg_and_die("only one device may be specified");
1207 file_name = &arg[i+1]; /* "-Fdevice" ? */
1208 if (!file_name[0]) { /* nope, "-F device" */
1209 int p = k+1; /* argv[p] is argnext */
1210 file_name = argnext;
1212 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1213 /* remove -F param from arg[vc] */
1215 while (argv[p]) { argv[p] = argv[p+1]; ++p; }
1219 goto invalid_argument;
1226 mp = find_mode(arg);
1228 stty_state &= ~STTY_noargs;
1232 cp = find_control(arg);
1235 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1236 /* called for the side effect of xfunc death only */
1237 set_control_char_or_die(cp, argnext, &mode);
1238 stty_state &= ~STTY_noargs;
1243 param = find_param(arg);
1244 if (param & param_need_arg) {
1246 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1254 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1256 # endif /* else fall-through */
1262 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1269 /* called for the side effect of xfunc death only */
1270 set_speed_or_die(input_speed, argnext, &mode);
1273 /* called for the side effect of xfunc death only */
1274 set_speed_or_die(output_speed, argnext, &mode);
1277 if (recover_mode(arg, &mode) == 1) break;
1278 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1280 bb_error_msg_and_die("invalid argument '%s'", arg);
1282 stty_state &= ~STTY_noargs;
1285 /* Specifying both -a and -g is an error */
1286 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1287 (STTY_verbose_output | STTY_recoverable_output))
1288 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
1289 /* Specifying -a or -g with non-options is an error */
1290 if (!(stty_state & STTY_noargs) &&
1291 (stty_state & (STTY_verbose_output | STTY_recoverable_output)))
1292 bb_error_msg_and_die("modes may not be set when specifying an output style");
1294 /* Now it is safe to start doing things */
1297 G.device_name = file_name;
1298 fd = xopen(G.device_name, O_RDONLY | O_NONBLOCK);
1299 if (fd != STDIN_FILENO) {
1300 dup2(fd, STDIN_FILENO);
1303 fdflags = fcntl(STDIN_FILENO, F_GETFL);
1305 fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
1306 perror_on_device_and_die("%s: cannot reset non-blocking mode");
1309 /* Initialize to all zeroes so there is no risk memcmp will report a
1310 spurious difference in an uninitialized portion of the structure */
1311 memset(&mode, 0, sizeof(mode));
1312 if (tcgetattr(STDIN_FILENO, &mode))
1313 perror_on_device_and_die("%s");
1315 if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
1316 get_terminal_width_height(STDOUT_FILENO, &G.max_col, NULL);
1317 output_func(&mode, display_all);
1318 return EXIT_SUCCESS;
1321 /* Second pass: perform actions */
1324 const struct mode_info *mp;
1325 const struct control_info *cp;
1326 const char *arg = argv[k];
1327 const char *argnext = argv[k+1];
1330 if (arg[0] == '-') {
1331 mp = find_mode(arg+1);
1333 set_mode(mp, 1 /* reversed */, &mode);
1334 stty_state |= STTY_require_set_attr;
1336 /* It is an option - already parsed. Skip it */
1340 mp = find_mode(arg);
1342 set_mode(mp, 0 /* non-reversed */, &mode);
1343 stty_state |= STTY_require_set_attr;
1347 cp = find_control(arg);
1350 set_control_char_or_die(cp, argnext, &mode);
1351 stty_state |= STTY_require_set_attr;
1355 param = find_param(arg);
1356 if (param & param_need_arg) {
1363 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1364 stty_state |= STTY_require_set_attr;
1369 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1372 display_window_size(0);
1375 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1379 display_speed(&mode, 0);
1382 set_speed_or_die(input_speed, argnext, &mode);
1383 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1386 set_speed_or_die(output_speed, argnext, &mode);
1387 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1390 if (recover_mode(arg, &mode) == 1)
1391 stty_state |= STTY_require_set_attr;
1392 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
1393 set_speed_or_die(both_speeds, arg, &mode);
1394 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1395 } /* else - impossible (caught in the first pass):
1396 bb_error_msg_and_die("invalid argument '%s'", arg); */
1400 if (stty_state & STTY_require_set_attr) {
1401 struct termios new_mode;
1403 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1404 perror_on_device_and_die("%s");
1406 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1407 it performs *any* of the requested operations. This means it
1408 can report 'success' when it has actually failed to perform
1409 some proper subset of the requested operations. To detect
1410 this partial failure, get the current terminal attributes and
1411 compare them to the requested ones */
1413 /* Initialize to all zeroes so there is no risk memcmp will report a
1414 spurious difference in an uninitialized portion of the structure */
1415 memset(&new_mode, 0, sizeof(new_mode));
1416 if (tcgetattr(STDIN_FILENO, &new_mode))
1417 perror_on_device_and_die("%s");
1419 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1421 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1422 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1423 sometimes (m1 != m2). The only difference is in the four bits
1424 of the c_cflag field corresponding to the baud rate. To save
1425 Sun users a little confusion, don't report an error if this
1426 happens. But suppress the error only if we haven't tried to
1427 set the baud rate explicitly -- otherwise we'd never give an
1428 error for a true failure to set the baud rate */
1430 new_mode.c_cflag &= (~CIBAUD);
1431 if ((stty_state & STTY_speed_was_set)
1432 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1434 perror_on_device_and_die("%s: cannot perform all requested operations");
1438 return EXIT_SUCCESS;