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 static const char evenp [] = "evenp";
131 static const char raw [] = "raw";
132 static const char stty_min [] = "min";
133 static const char stty_time [] = "time";
134 static const char stty_swtch[] = "swtch";
135 static const char stty_eol [] = "eol";
136 static const char stty_eof [] = "eof";
137 static const char parity [] = "parity";
138 static const char stty_oddp [] = "oddp";
139 static const char stty_nl [] = "nl";
140 static const char stty_ek [] = "ek";
141 static const char stty_sane [] = "sane";
142 static const char cbreak [] = "cbreak";
143 static const char stty_pass8[] = "pass8";
144 static const char litout [] = "litout";
145 static const char cooked [] = "cooked";
146 static const char decctlq [] = "decctlq";
147 static const char stty_tabs [] = "tabs";
148 static const char stty_lcase[] = "lcase";
149 static const char stty_LCASE[] = "LCASE";
150 static const char stty_crt [] = "crt";
151 static const char stty_dec [] = "dec";
153 /* Flags for `struct mode_info' */
154 #define SANE_SET 1 /* Set in `sane' mode */
155 #define SANE_UNSET 2 /* Unset in `sane' mode */
156 #define REV 4 /* Can be turned off by prepending `-' */
157 #define OMIT 8 /* Don't display value */
161 const char *name; /* Name given on command line */
162 /* enum mode_type type; */
163 char type; /* Which structure element to change */
164 char flags; /* Setting and display options */
165 unsigned short mask; /* Other bits to turn off for this mode */
166 unsigned long bits; /* Bits to set for this mode */
168 #define EMT(t) ((enum mode_type)(t))
170 #define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B }
172 static const struct mode_info mode_info[] = {
173 MI_ENTRY("parenb", control, REV, PARENB, 0 ),
174 MI_ENTRY("parodd", control, REV, PARODD, 0 ),
175 MI_ENTRY("cs5", control, 0, CS5, CSIZE),
176 MI_ENTRY("cs6", control, 0, CS6, CSIZE),
177 MI_ENTRY("cs7", control, 0, CS7, CSIZE),
178 MI_ENTRY("cs8", control, 0, CS8, CSIZE),
179 MI_ENTRY("hupcl", control, REV, HUPCL, 0 ),
180 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ),
181 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ),
182 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ),
183 MI_ENTRY("clocal", control, REV, CLOCAL, 0 ),
185 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ),
187 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ),
188 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ),
189 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ),
190 MI_ENTRY("parmrk", input, REV, PARMRK, 0 ),
191 MI_ENTRY("inpck", input, REV, INPCK, 0 ),
192 MI_ENTRY("istrip", input, REV, ISTRIP, 0 ),
193 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ),
194 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ),
195 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ),
196 MI_ENTRY("ixon", input, REV, IXON, 0 ),
197 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ),
198 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 ),
200 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ),
203 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ),
206 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ),
208 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ),
210 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ),
213 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ),
216 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ),
219 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ),
222 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ),
225 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ),
228 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ),
231 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY),
232 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY),
235 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY),
236 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY),
237 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY),
238 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY),
242 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY),
243 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY),
244 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY),
245 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY),
248 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ),
253 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY),
254 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY),
257 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY),
258 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY),
261 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY),
262 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY),
264 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ),
265 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ),
267 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ),
269 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ),
270 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ),
271 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 ),
272 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ),
273 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ),
274 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ),
276 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ),
279 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ),
282 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ),
283 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 ),
286 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ),
287 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 ),
290 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ),
291 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 ),
293 MI_ENTRY(evenp, combination, REV | OMIT, 0, 0 ),
294 MI_ENTRY(parity, combination, REV | OMIT, 0, 0 ),
295 MI_ENTRY(stty_oddp, combination, REV | OMIT, 0, 0 ),
296 MI_ENTRY(stty_nl, combination, REV | OMIT, 0, 0 ),
297 MI_ENTRY(stty_ek, combination, OMIT, 0, 0 ),
298 MI_ENTRY(stty_sane, combination, OMIT, 0, 0 ),
299 MI_ENTRY(cooked, combination, REV | OMIT, 0, 0 ),
300 MI_ENTRY(raw, combination, REV | OMIT, 0, 0 ),
301 MI_ENTRY(stty_pass8, combination, REV | OMIT, 0, 0 ),
302 MI_ENTRY(litout, combination, REV | OMIT, 0, 0 ),
303 MI_ENTRY(cbreak, combination, REV | OMIT, 0, 0 ),
305 MI_ENTRY(decctlq, combination, REV | OMIT, 0, 0 ),
307 #if defined(TABDLY) || defined(OXTABS)
308 MI_ENTRY(stty_tabs, combination, REV | OMIT, 0, 0 ),
310 #if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
311 MI_ENTRY(stty_lcase, combination, REV | OMIT, 0, 0 ),
312 MI_ENTRY(stty_LCASE, combination, REV | OMIT, 0, 0 ),
314 MI_ENTRY(stty_crt, combination, OMIT, 0, 0 ),
315 MI_ENTRY(stty_dec, combination, OMIT, 0, 0 ),
319 NUM_mode_info = (sizeof(mode_info) / sizeof(mode_info[0]))
322 /* Control character settings */
323 struct control_info {
324 const char *name; /* Name given on command line */
325 unsigned char saneval; /* Value to set for `stty sane' */
326 unsigned char offset; /* Offset in c_cc */
329 /* Control characters */
331 static const struct control_info control_info[] = {
332 {"intr", CINTR, VINTR},
333 {"quit", CQUIT, VQUIT},
334 {"erase", CERASE, VERASE},
335 {"kill", CKILL, VKILL},
336 {stty_eof, CEOF, VEOF},
337 {stty_eol, CEOL, VEOL},
339 {"eol2", CEOL2, VEOL2},
342 {stty_swtch, CSWTCH, VSWTCH},
344 {"start", CSTART, VSTART},
345 {"stop", CSTOP, VSTOP},
346 {"susp", CSUSP, VSUSP},
348 {"dsusp", CDSUSP, VDSUSP},
351 {"rprnt", CRPRNT, VREPRINT},
354 {"werase", CWERASE, VWERASE},
357 {"lnext", CLNEXT, VLNEXT},
360 {"flush", CFLUSHO, VFLUSHO},
363 {"status", CSTATUS, VSTATUS},
365 /* These must be last because of the display routines */
367 {stty_time, 0, VTIME},
371 NUM_control_info = (sizeof(control_info) / sizeof(control_info[0]))
374 /* The width of the screen, for output wrapping */
377 /* Current position, to know when to wrap */
378 static int current_col;
380 static const char * visible(unsigned int ch);
381 static int recover_mode(const char *arg, struct termios *mode);
382 static int screen_columns(void);
383 static void set_mode(const struct mode_info *info,
384 int reversed, struct termios *mode);
385 static speed_t string_to_baud(const char *arg);
386 static tcflag_t* mode_type_flag(enum mode_type type, const struct termios *mode);
387 static void display_all(const struct termios *mode);
388 static void display_changed(const struct termios *mode);
389 static void display_recoverable(const struct termios *mode);
390 static void display_speed(const struct termios *mode, int fancy);
391 static void display_window_size(int fancy);
392 static void sane_mode(struct termios *mode);
393 static void set_control_char(const struct control_info *info,
394 const char *arg, struct termios *mode);
395 static void set_speed(enum speed_setting type,
396 const char *arg, struct termios *mode);
397 static void set_window_size(int rows, int cols);
399 static const char *device_name = bb_msg_standard_input;
401 static ATTRIBUTE_NORETURN void perror_on_device(const char *fmt)
403 bb_perror_msg_and_die(fmt, device_name);
406 /* No, inline won't be as efficient (gcc 3.4.3) */
407 #define streq(a,b) (!strcmp((a),(b)))
409 /* Print format string MESSAGE and optional args.
410 Wrap to next line first if it won't fit.
411 Print a space first unless MESSAGE will start a new line */
412 static void wrapf(const char *message, ...)
418 va_start(args, message);
419 vsnprintf(buf, sizeof(buf), message, args);
421 buflen = strlen(buf);
424 if (current_col > 0) {
426 if (current_col + buflen >= max_col) {
430 if (buf[0] != '\n') putchar(' ');
433 current_col += buflen;
434 if (buf[buflen-1] == '\n')
438 static const struct suffix_mult stty_suffixes[] = {
445 static const struct mode_info *find_mode(const char *name)
448 for (i = 0; i < NUM_mode_info; ++i)
449 if (streq(name, mode_info[i].name))
450 return &mode_info[i];
454 static const struct control_info *find_control(const char *name)
457 for (i = 0; i < NUM_control_info; ++i)
458 if (streq(name, control_info[i].name))
459 return &control_info[i];
464 param_need_arg = 0x80,
465 param_line = 1 | 0x80,
466 param_rows = 2 | 0x80,
467 param_cols = 3 | 0x80,
469 param_ispeed = 5 | 0x80,
470 param_ospeed = 6 | 0x80,
474 static int find_param(const char *name)
477 if (streq(name, "line")) return param_line;
480 if (streq(name, "rows")) return param_rows;
481 if (streq(name, "cols")) return param_cols;
482 if (streq(name, "columns")) return param_cols;
483 if (streq(name, "size")) return param_size;
485 if (streq(name, "ispeed")) return param_ispeed;
486 if (streq(name, "ospeed")) return param_ospeed;
487 if (streq(name, "speed")) return param_speed;
492 int stty_main(int argc, char **argv)
495 void (*output_func)(const struct termios *);
496 const char *file_name = NULL;
497 int require_set_attr;
500 int recoverable_output;
504 output_func = display_changed;
507 require_set_attr = 0;
509 recoverable_output = 0;
511 /* First pass: only parse/verify command line params */
514 const struct mode_info *mp;
515 const struct control_info *cp;
516 const char *arg = argv[k];
517 const char *argnext = argv[k+1];
522 mp = find_mode(arg+1);
524 if (!(mp->flags & REV))
525 bb_error_msg_and_die("invalid argument '%s'", arg);
529 /* It is an option - parse it */
535 output_func = display_all;
538 recoverable_output = 1;
539 output_func = display_recoverable;
543 bb_error_msg_and_die("only one device may be specified");
544 file_name = &arg[i+1]; /* "-Fdevice" ? */
545 if (!file_name[0]) { /* nope, "-F device" */
546 int p = k+1; /* argv[p] is argnext */
549 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
550 /* remove -F param from arg[vc] */
552 while (argv[p+1]) { argv[p] = argv[p+1]; ++p; }
556 bb_error_msg_and_die("invalid argument '%s'", arg);
569 cp = find_control(arg);
572 bb_error_msg_and_die(bb_msg_requires_arg, arg);
578 param = find_param(arg);
579 if (param & param_need_arg) {
581 bb_error_msg_and_die(bb_msg_requires_arg, arg);
589 bb_xparse_number(argnext, stty_suffixes);
591 # endif /* else fall-through */
596 bb_xparse_number(argnext, stty_suffixes);
605 if (recover_mode(arg, &mode) == 1) break;
606 if (string_to_baud(arg) != (speed_t) -1) break;
607 bb_error_msg_and_die("invalid argument '%s'", arg);
612 /* Specifying both -a and -g is an error */
613 if (verbose_output && recoverable_output)
614 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
615 /* Specifying -a or -g with non-options is an error */
616 if (!noargs && (verbose_output || recoverable_output))
617 bb_error_msg_and_die("modes may not be set when specifying an output style");
619 /* Now it is safe to start doing things */
622 device_name = file_name;
623 fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
628 fdflags = fcntl(STDIN_FILENO, F_GETFL);
629 if (fdflags == -1 || fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
630 perror_on_device("%s: couldn't reset non-blocking mode");
633 /* Initialize to all zeroes so there is no risk memcmp will report a
634 spurious difference in an uninitialized portion of the structure */
635 memset(&mode, 0, sizeof(mode));
636 if (tcgetattr(STDIN_FILENO, &mode))
637 perror_on_device("%s");
639 if (verbose_output || recoverable_output || noargs) {
640 max_col = screen_columns();
646 /* Second pass: perform actions */
649 const struct mode_info *mp;
650 const struct control_info *cp;
651 const char *arg = argv[k];
652 const char *argnext = argv[k+1];
656 mp = find_mode(arg+1);
658 set_mode(mp, 1 /* reversed */, &mode);
660 /* It is an option - already parsed. Skip it */
666 set_mode(mp, 0 /* non-reversed */, &mode);
670 cp = find_control(arg);
673 set_control_char(cp, argnext, &mode);
677 param = find_param(arg);
678 if (param & param_need_arg) {
685 mode.c_line = bb_xparse_number(argnext, stty_suffixes);
686 require_set_attr = 1;
691 set_window_size(-1, (int) bb_xparse_number(argnext, stty_suffixes));
694 max_col = screen_columns();
696 display_window_size(0);
699 set_window_size((int) bb_xparse_number(argnext, stty_suffixes), -1);
703 set_speed(input_speed, argnext, &mode);
705 require_set_attr = 1;
708 set_speed(output_speed, argnext, &mode);
710 require_set_attr = 1;
713 max_col = screen_columns();
714 display_speed(&mode, 0);
717 if (recover_mode(arg, &mode) == 1)
718 require_set_attr = 1;
719 else /* true: if (string_to_baud(arg) != (speed_t) -1) */ {
720 set_speed(both_speeds, arg, &mode);
722 require_set_attr = 1;
723 } /* else - impossible (caught in the first pass):
724 bb_error_msg_and_die("invalid argument '%s'", arg); */
728 if (require_set_attr) {
729 struct termios new_mode;
731 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
732 perror_on_device("%s");
734 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
735 it performs *any* of the requested operations. This means it
736 can report `success' when it has actually failed to perform
737 some proper subset of the requested operations. To detect
738 this partial failure, get the current terminal attributes and
739 compare them to the requested ones */
741 /* Initialize to all zeroes so there is no risk memcmp will report a
742 spurious difference in an uninitialized portion of the structure */
743 memset(&new_mode, 0, sizeof(new_mode));
744 if (tcgetattr(STDIN_FILENO, &new_mode))
745 perror_on_device("%s");
747 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
749 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
750 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
751 sometimes (m1 != m2). The only difference is in the four bits
752 of the c_cflag field corresponding to the baud rate. To save
753 Sun users a little confusion, don't report an error if this
754 happens. But suppress the error only if we haven't tried to
755 set the baud rate explicitly -- otherwise we'd never give an
756 error for a true failure to set the baud rate */
758 new_mode.c_cflag &= (~CIBAUD);
759 if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
761 perror_on_device ("%s: unable to perform all requested operations");
768 /* Save set_mode from #ifdef forest plague */
804 set_mode(const struct mode_info *info, int reversed, struct termios *mode)
808 bitsp = mode_type_flag(EMT(info->type), mode);
811 /* Combination mode */
812 if (info->name == evenp || info->name == parity) {
814 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
817 (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
818 } else if (info->name == stty_oddp) {
820 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
823 (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
824 } else if (info->name == stty_nl) {
826 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
827 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
829 mode->c_iflag = mode->c_iflag & ~ICRNL;
830 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
832 } else if (info->name == stty_ek) {
833 mode->c_cc[VERASE] = CERASE;
834 mode->c_cc[VKILL] = CKILL;
835 } else if (info->name == stty_sane)
837 else if (info->name == cbreak) {
839 mode->c_lflag |= ICANON;
841 mode->c_lflag &= ~ICANON;
842 } else if (info->name == stty_pass8) {
844 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
845 mode->c_iflag |= ISTRIP;
847 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
848 mode->c_iflag &= ~ISTRIP;
850 } else if (info->name == litout) {
852 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
853 mode->c_iflag |= ISTRIP;
854 mode->c_oflag |= OPOST;
856 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
857 mode->c_iflag &= ~ISTRIP;
858 mode->c_oflag &= ~OPOST;
860 } else if (info->name == raw || info->name == cooked) {
861 if ((info->name[0] == 'r' && reversed)
862 || (info->name[0] == 'c' && !reversed)) {
864 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
865 mode->c_oflag |= OPOST;
866 mode->c_lflag |= ISIG | ICANON;
868 mode->c_cc[VEOF] = CEOF;
871 mode->c_cc[VEOL] = CEOL;
876 mode->c_oflag &= ~OPOST;
877 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
878 mode->c_cc[VMIN] = 1;
879 mode->c_cc[VTIME] = 0;
882 else if (IXANY && info->name == decctlq) {
884 mode->c_iflag |= IXANY;
886 mode->c_iflag &= ~IXANY;
888 else if (TABDLY && info->name == stty_tabs) {
890 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
892 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
894 else if (OXTABS && info->name == stty_tabs) {
896 mode->c_oflag = mode->c_oflag | OXTABS;
898 mode->c_oflag = mode->c_oflag & ~OXTABS;
900 else if (XCASE && IUCLC && OLCUC
901 && (info->name == stty_lcase || info->name == stty_LCASE)) {
903 mode->c_lflag &= ~XCASE;
904 mode->c_iflag &= ~IUCLC;
905 mode->c_oflag &= ~OLCUC;
907 mode->c_lflag |= XCASE;
908 mode->c_iflag |= IUCLC;
909 mode->c_oflag |= OLCUC;
912 else if (info->name == stty_crt)
913 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
914 else if (info->name == stty_dec) {
915 mode->c_cc[VINTR] = 3; /* ^C */
916 mode->c_cc[VERASE] = 127; /* DEL */
917 mode->c_cc[VKILL] = 21; /* ^U */
918 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
919 if (IXANY) mode->c_iflag &= ~IXANY;
922 *bitsp = *bitsp & ~((unsigned long)info->mask) & ~info->bits;
924 *bitsp = (*bitsp & ~((unsigned long)info->mask)) | info->bits;
928 set_control_char(const struct control_info *info, const char *arg,
929 struct termios *mode)
933 if (info->name == stty_min || info->name == stty_time)
934 value = bb_xparse_number(arg, stty_suffixes);
935 else if (arg[0] == '\0' || arg[1] == '\0')
937 else if (streq(arg, "^-") || streq(arg, "undef"))
938 value = _POSIX_VDISABLE;
939 else if (arg[0] == '^' && arg[1] != '\0') { /* Ignore any trailing junk */
943 value = arg[1] & ~0140; /* Non-letters get weird results */
945 value = bb_xparse_number(arg, stty_suffixes);
946 mode->c_cc[info->offset] = value;
950 set_speed(enum speed_setting type, const char *arg, struct termios *mode)
954 baud = string_to_baud(arg);
956 if (type != output_speed) { /* either input or both */
957 cfsetispeed(mode, baud);
959 if (type != input_speed) { /* either output or both */
960 cfsetospeed(mode, baud);
966 static int get_win_size(int fd, struct winsize *win)
968 return ioctl(fd, TIOCGWINSZ, (char *) win);
972 set_window_size(int rows, int cols)
976 if (get_win_size(STDIN_FILENO, &win)) {
978 perror_on_device("%s");
979 memset(&win, 0, sizeof(win));
988 /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
989 The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
990 This comment from sys/ttold.h describes Sun's twisted logic - a better
991 test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
992 At any rate, the problem is gone in Solaris 2.x */
994 if (win.ws_row == 0 || win.ws_col == 0) {
995 struct ttysize ttysz;
997 ttysz.ts_lines = win.ws_row;
998 ttysz.ts_cols = win.ws_col;
1000 win.ws_row = win.ws_col = 1;
1002 if ((ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win) != 0)
1003 || (ioctl(STDIN_FILENO, TIOCSSIZE, (char *) &ttysz) != 0)) {
1004 perror_on_device("%s");
1010 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
1011 perror_on_device("%s");
1014 static void display_window_size(int fancy)
1016 const char *fmt_str = "%s" "\0" "%s: no size information for this device";
1019 if (get_win_size(STDIN_FILENO, &win)) {
1020 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
1021 perror_on_device(fmt_str);
1024 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
1025 win.ws_row, win.ws_col);
1030 static int screen_columns(void)
1038 /* With Solaris 2.[123], this ioctl fails and errno is set to
1039 EINVAL for telnet (but not rlogin) sessions.
1040 On ISC 3.0, it fails for the console and the serial port
1041 (but it works for ptys).
1042 It can also fail on any system when stdout isn't a tty.
1043 In case of any failure, just use the default */
1044 if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0)
1049 if ((s = getenv("COLUMNS"))) {
1055 static tcflag_t *mode_type_flag(enum mode_type type, const struct termios *mode)
1057 static const unsigned char tcflag_offsets[] = {
1058 offsetof(struct termios, c_cflag), /* control */
1059 offsetof(struct termios, c_iflag), /* input */
1060 offsetof(struct termios, c_oflag), /* output */
1061 offsetof(struct termios, c_lflag) /* local */
1064 if (((unsigned int) type) <= local) {
1065 return (tcflag_t *)(((char *) mode) + tcflag_offsets[(int)type]);
1070 static void display_changed(const struct termios *mode)
1075 enum mode_type prev_type = control;
1077 display_speed(mode, 1);
1079 wrapf("line = %d;\n", mode->c_line);
1084 for (i = 0; control_info[i].name != stty_min; ++i) {
1085 if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
1087 /* If swtch is the same as susp, don't print both */
1089 if (control_info[i].name == stty_swtch)
1092 /* If eof uses the same slot as min, only print whichever applies */
1094 if ((mode->c_lflag & ICANON) == 0
1095 && (control_info[i].name == stty_eof
1096 || control_info[i].name == stty_eol)) continue;
1098 wrapf("%s = %s;", control_info[i].name,
1099 visible(mode->c_cc[control_info[i].offset]));
1101 if ((mode->c_lflag & ICANON) == 0) {
1102 wrapf("min = %d; time = %d;", (int) mode->c_cc[VMIN],
1103 (int) mode->c_cc[VTIME]);
1105 if (current_col) wrapf("\n");
1107 for (i = 0; i < NUM_mode_info; ++i) {
1108 if (mode_info[i].flags & OMIT)
1110 if (EMT(mode_info[i].type) != prev_type) {
1111 if (current_col) wrapf("\n");
1112 prev_type = EMT(mode_info[i].type);
1115 bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
1116 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1117 if ((*bitsp & mask) == mode_info[i].bits) {
1118 if (mode_info[i].flags & SANE_UNSET) {
1119 wrapf("%s", mode_info[i].name);
1121 } else if ((mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)) {
1122 wrapf("-%s", mode_info[i].name);
1125 if (current_col) wrapf("\n");
1129 display_all(const struct termios *mode)
1134 enum mode_type prev_type = control;
1136 display_speed(mode, 1);
1138 display_window_size(1);
1141 wrapf("line = %d;\n", mode->c_line);
1146 for (i = 0; control_info[i].name != stty_min; ++i) {
1147 /* If swtch is the same as susp, don't print both */
1149 if (control_info[i].name == stty_swtch)
1152 /* If eof uses the same slot as min, only print whichever applies */
1154 if ((mode->c_lflag & ICANON) == 0
1155 && (control_info[i].name == stty_eof
1156 || control_info[i].name == stty_eol)) continue;
1158 wrapf("%s = %s;", control_info[i].name,
1159 visible(mode->c_cc[control_info[i].offset]));
1162 if ((mode->c_lflag & ICANON) == 0)
1164 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1165 if (current_col) wrapf("\n");
1167 for (i = 0; i < NUM_mode_info; ++i) {
1168 if (mode_info[i].flags & OMIT)
1170 if (EMT(mode_info[i].type) != prev_type) {
1173 prev_type = EMT(mode_info[i].type);
1176 bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
1177 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1178 if ((*bitsp & mask) == mode_info[i].bits)
1179 wrapf("%s", mode_info[i].name);
1180 else if (mode_info[i].flags & REV)
1181 wrapf("-%s", mode_info[i].name);
1183 if (current_col) wrapf("\n");
1186 static void display_speed(const struct termios *mode, int fancy)
1189 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
1190 unsigned long ispeed, ospeed;
1192 ospeed = ispeed = cfgetispeed(mode);
1193 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
1194 ispeed = ospeed; /* in case ispeed was 0 */
1196 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
1198 if (fancy) fmt_str += 9;
1199 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
1202 static void display_recoverable(const struct termios *mode)
1206 printf("%lx:%lx:%lx:%lx",
1207 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1208 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1209 for (i = 0; i < NCCS; ++i)
1210 printf(":%x", (unsigned int) mode->c_cc[i]);
1214 static int recover_mode(const char *arg, struct termios *mode)
1218 unsigned long iflag, oflag, cflag, lflag;
1220 /* Scan into temporaries since it is too much trouble to figure out
1221 the right format for `tcflag_t' */
1222 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
1223 &iflag, &oflag, &cflag, &lflag, &n) != 4)
1225 mode->c_iflag = iflag;
1226 mode->c_oflag = oflag;
1227 mode->c_cflag = cflag;
1228 mode->c_lflag = lflag;
1230 for (i = 0; i < NCCS; ++i) {
1231 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
1233 mode->c_cc[i] = chr;
1237 /* Fail if there are too many fields */
1244 static speed_t string_to_baud(const char *arg)
1246 return tty_value_to_baud(bb_xparse_number(arg, 0));
1249 static void sane_mode(struct termios *mode)
1254 for (i = 0; i < NUM_control_info; ++i) {
1256 if (control_info[i].name == stty_min)
1259 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1262 for (i = 0; i < NUM_mode_info; ++i) {
1263 if (mode_info[i].flags & SANE_SET) {
1264 bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
1265 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
1266 | mode_info[i].bits;
1267 } else if (mode_info[i].flags & SANE_UNSET) {
1268 bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
1269 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
1270 & ~mode_info[i].bits;
1275 /* Return a string that is the printable representation of character CH */
1276 /* Adapted from `cat' by Torbjorn Granlund */
1278 static const char *visible(unsigned int ch)
1280 static char buf[10];
1283 if (ch == _POSIX_VDISABLE) {
1296 } else if (ch < 127) {