stty: rearrange functions, avoiding the need in forward declarations.
authorDenis Vlasenko <vda.linux@googlemail.com>
Fri, 12 Jan 2007 21:02:04 +0000 (21:02 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Fri, 12 Jan 2007 21:02:04 +0000 (21:02 -0000)
No other code chages.

coreutils/stty.c

index b489414297fb6442b7ffb41c7d5ca67ce73437b1..93919b33afc61ef471f8d1928996487a34491fa3 100644 (file)
@@ -637,296 +637,141 @@ static int find_param(const char *name)
        return 0;
 }
 
-
-static int recover_mode(const char *arg, struct termios *mode);
-static void set_mode(const struct mode_info *info,
-                               int reversed, struct termios *mode);
-static void display_all(const struct termios *mode);
-static void display_changed(const struct termios *mode);
-static void display_recoverable(const struct termios *mode);
-static void display_speed(const struct termios *mode, int fancy);
-static void sane_mode(struct termios *mode);
-static void set_control_char_or_die(const struct control_info *info,
-                               const char *arg, struct termios *mode);
-
-int stty_main(int argc, char **argv)
+static int recover_mode(const char *arg, struct termios *mode)
 {
-       struct termios mode;
-       void (*output_func)(const struct termios *);
-       const char *file_name = NULL;
-       int require_set_attr;
-       int speed_was_set;
-       int verbose_output;
-       int recoverable_output;
-       int noargs;
-       int k;
-
-       output_func = display_changed;
-       noargs = 1;
-       speed_was_set = 0;
-       require_set_attr = 0;
-       verbose_output = 0;
-       recoverable_output = 0;
-
-       /* First pass: only parse/verify command line params */
-       k = 0;
-       while (argv[++k]) {
-               const struct mode_info *mp;
-               const struct control_info *cp;
-               const char *arg = argv[k];
-               const char *argnext = argv[k+1];
-               int param;
-
-               if (arg[0] == '-') {
-                       int i;
-                       mp = find_mode(arg+1);
-                       if (mp) {
-                               if (!(mp->flags & REV))
-                                       bb_error_msg_and_die("invalid argument '%s'", arg);
-                               noargs = 0;
-                               continue;
-                       }
-                       /* It is an option - parse it */
-                       i = 0;
-                       while (arg[++i]) {
-                               switch (arg[i]) {
-                               case 'a':
-                                       verbose_output = 1;
-                                       output_func = display_all;
-                                       break;
-                               case 'g':
-                                       recoverable_output = 1;
-                                       output_func = display_recoverable;
-                                       break;
-                               case 'F':
-                                       if (file_name)
-                                               bb_error_msg_and_die("only one device may be specified");
-                                       file_name = &arg[i+1]; /* "-Fdevice" ? */
-                                       if (!file_name[0]) { /* nope, "-F device" */
-                                               int p = k+1; /* argv[p] is argnext */
-                                               file_name = argnext;
-                                               if (!file_name)
-                                                       bb_error_msg_and_die(bb_msg_requires_arg, "-F");
-                                               /* remove -F param from arg[vc] */
-                                               --argc;
-                                               while (argv[p]) { argv[p] = argv[p+1]; ++p; }
-                                       }
-                                       goto end_option;
-                               default:
-                                       bb_error_msg_and_die("invalid argument '%s'", arg);
-                               }
-                       }
-end_option:
-                       continue;
-               }
-
-               mp = find_mode(arg);
-               if (mp) {
-                       noargs = 0;
-                       continue;
-               }
-
-               cp = find_control(arg);
-               if (cp) {
-                       if (!argnext)
-                               bb_error_msg_and_die(bb_msg_requires_arg, arg);
-                       /* called for the side effect of xfunc death only */
-                       set_control_char_or_die(cp, argnext, &mode);
-                       noargs = 0;
-                       ++k;
-                       continue;
-               }
-
-               param = find_param(arg);
-               if (param & param_need_arg) {
-                       if (!argnext)
-                               bb_error_msg_and_die(bb_msg_requires_arg, arg);
-                       ++k;
-               }
-
-               switch (param) {
-#ifdef HAVE_C_LINE
-               case param_line:
-# ifndef TIOCGWINSZ
-                       xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
-                       break;
-# endif /* else fall-through */
-#endif
-#ifdef TIOCGWINSZ
-               case param_rows:
-               case param_cols:
-                       xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
-                       break;
-               case param_size:
-#endif
-               case param_speed:
-                       break;
-               case param_ispeed:
-                       /* called for the side effect of xfunc death only */
-                       set_speed_or_die(input_speed, argnext, &mode);
-                       break;
-               case param_ospeed:
-                       /* called for the side effect of xfunc death only */
-                       set_speed_or_die(output_speed, argnext, &mode);
-                       break;
-               default:
-                       if (recover_mode(arg, &mode) == 1) break;
-                       if (string_to_baud_or_die(arg) != (speed_t) -1) break;
-                       bb_error_msg_and_die("invalid argument '%s'", arg);
-               }
-               noargs = 0;
-       }
-
-       /* Specifying both -a and -g is an error */
-       if (verbose_output && recoverable_output)
-               bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
-       /* Specifying -a or -g with non-options is an error */
-       if (!noargs && (verbose_output || recoverable_output))
-               bb_error_msg_and_die("modes may not be set when specifying an output style");
+       int i, n;
+       unsigned int chr;
+       unsigned long iflag, oflag, cflag, lflag;
 
-       /* Now it is safe to start doing things */
-       if (file_name) {
-               int fd, fdflags;
-               device_name = file_name;
-               fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
-               if (fd != STDIN_FILENO) {
-                       dup2(fd, STDIN_FILENO);
-                       close(fd);
-               }
-               fdflags = fcntl(STDIN_FILENO, F_GETFL);
-               if (fdflags == -1 || fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
-                       perror_on_device_and_die("%s: cannot reset non-blocking mode");
+       /* Scan into temporaries since it is too much trouble to figure out
+          the right format for 'tcflag_t' */
+       if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
+                          &iflag, &oflag, &cflag, &lflag, &n) != 4)
+               return 0;
+       mode->c_iflag = iflag;
+       mode->c_oflag = oflag;
+       mode->c_cflag = cflag;
+       mode->c_lflag = lflag;
+       arg += n;
+       for (i = 0; i < NCCS; ++i) {
+               if (sscanf(arg, ":%x%n", &chr, &n) != 1)
+                       return 0;
+               mode->c_cc[i] = chr;
+               arg += n;
        }
 
-       /* Initialize to all zeroes so there is no risk memcmp will report a
-          spurious difference in an uninitialized portion of the structure */
-       memset(&mode, 0, sizeof(mode));
-       if (tcgetattr(STDIN_FILENO, &mode))
-               perror_on_device_and_die("%s");
-
-       if (verbose_output || recoverable_output || noargs) {
-               max_col = screen_columns_or_die();
-               output_func(&mode);
-               return EXIT_SUCCESS;
-       }
+       /* Fail if there are too many fields */
+       if (*arg != '\0')
+               return 0;
 
-       /* Second pass: perform actions */
-       k = 0;
-       while (argv[++k]) {
-               const struct mode_info *mp;
-               const struct control_info *cp;
-               const char *arg = argv[k];
-               const char *argnext = argv[k+1];
-               int param;
+       return 1;
+}
 
-               if (arg[0] == '-') {
-                       mp = find_mode(arg+1);
-                       if (mp) {
-                               set_mode(mp, 1 /* reversed */, &mode);
-                       }
-                       /* It is an option - already parsed. Skip it */
-                       continue;
-               }
+static void display_recoverable(const struct termios *mode)
+{
+       int i;
+       printf("%lx:%lx:%lx:%lx",
+                  (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
+                  (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
+       for (i = 0; i < NCCS; ++i)
+               printf(":%x", (unsigned int) mode->c_cc[i]);
+       putchar('\n');
+}
 
-               mp = find_mode(arg);
-               if (mp) {
-                       set_mode(mp, 0 /* non-reversed */, &mode);
-                       continue;
-               }
+static void display_speed(const struct termios *mode, int fancy)
+{
+                            //01234567 8 9
+       const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
+       unsigned long ispeed, ospeed;
 
-               cp = find_control(arg);
-               if (cp) {
-                       ++k;
-                       set_control_char_or_die(cp, argnext, &mode);
-                       continue;
-               }
+       ospeed = ispeed = cfgetispeed(mode);
+       if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
+               ispeed = ospeed;                /* in case ispeed was 0 */
+                        //0123 4 5 6 7 8 9
+               fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
+       }
+       if (fancy) fmt_str += 9;
+       wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
+}
 
-               param = find_param(arg);
-               if (param & param_need_arg) {
-                       ++k;
-               }
+static void display_all(const struct termios *mode)
+{
+       int i;
+       tcflag_t *bitsp;
+       unsigned long mask;
+       int prev_type = control;
 
-               switch (param) {
+       display_speed(mode, 1);
+       display_window_size(1);
 #ifdef HAVE_C_LINE
-               case param_line:
-                       mode.c_line = xatoul_sfx(argnext, stty_suffixes);
-                       require_set_attr = 1;
-                       break;
+       wrapf("line = %d;\n", mode->c_line);
+#else
+       wrapf("\n");
 #endif
-#ifdef TIOCGWINSZ
-               case param_cols:
-                       set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
-                       break;
-               case param_size:
-                       display_window_size(0);
-                       break;
-               case param_rows:
-                       set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
-                       break;
+
+       for (i = 0; control_info[i].name != stty_min; ++i) {
+               /* If swtch is the same as susp, don't print both */
+#if VSWTCH == VSUSP
+               if (control_info[i].name == stty_swtch)
+                       continue;
 #endif
-               case param_speed:
-                       display_speed(&mode, 0);
-                       break;
-               case param_ispeed:
-                       set_speed_or_die(input_speed, argnext, &mode);
-                       speed_was_set = 1;
-                       require_set_attr = 1;
-                       break;
-               case param_ospeed:
-                       set_speed_or_die(output_speed, argnext, &mode);
-                       speed_was_set = 1;
-                       require_set_attr = 1;
-                       break;
-               default:
-                       if (recover_mode(arg, &mode) == 1)
-                               require_set_attr = 1;
-                       else /* true: if (string_to_baud_or_die(arg) != (speed_t) -1) */ {
-                               set_speed_or_die(both_speeds, arg, &mode);
-                               speed_was_set = 1;
-                               require_set_attr = 1;
-                       } /* else - impossible (caught in the first pass):
-                               bb_error_msg_and_die("invalid argument '%s'", arg); */
-               }
+               /* If eof uses the same slot as min, only print whichever applies */
+#if VEOF == VMIN
+               if ((mode->c_lflag & ICANON) == 0
+                       && (control_info[i].name == stty_eof
+                               || control_info[i].name == stty_eol)) continue;
+#endif
+               wrapf("%s = %s;", control_info[i].name,
+                         visible(mode->c_cc[control_info[i].offset]));
        }
+#if VEOF == VMIN
+       if ((mode->c_lflag & ICANON) == 0)
+#endif
+               wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
+       if (current_col) wrapf("\n");
 
-       if (require_set_attr) {
-               struct termios new_mode;
-
-               if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
-                       perror_on_device_and_die("%s");
-
-               /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
-                  it performs *any* of the requested operations.  This means it
-                  can report 'success' when it has actually failed to perform
-                  some proper subset of the requested operations.  To detect
-                  this partial failure, get the current terminal attributes and
-                  compare them to the requested ones */
+       for (i = 0; i < NUM_mode_info; ++i) {
+               if (mode_info[i].flags & OMIT)
+                       continue;
+               if (mode_info[i].type != prev_type) {
+                       wrapf("\n");
+                       prev_type = mode_info[i].type;
+               }
 
-               /* Initialize to all zeroes so there is no risk memcmp will report a
-                  spurious difference in an uninitialized portion of the structure */
-               memset(&new_mode, 0, sizeof(new_mode));
-               if (tcgetattr(STDIN_FILENO, &new_mode))
-                       perror_on_device_and_die("%s");
+               bitsp = mode_type_flag(mode_info[i].type, mode);
+               mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
+               if ((*bitsp & mask) == mode_info[i].bits)
+                       wrapf("%s", mode_info[i].name);
+               else if (mode_info[i].flags & REV)
+                       wrapf("-%s", mode_info[i].name);
+       }
+       if (current_col) wrapf("\n");
+}
 
-               if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
-#ifdef CIBAUD
-                       /* SunOS 4.1.3 (at least) has the problem that after this sequence,
-                          tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
-                          sometimes (m1 != m2).  The only difference is in the four bits
-                          of the c_cflag field corresponding to the baud rate.  To save
-                          Sun users a little confusion, don't report an error if this
-                          happens.  But suppress the error only if we haven't tried to
-                          set the baud rate explicitly -- otherwise we'd never give an
-                          error for a true failure to set the baud rate */
+static void sane_mode(struct termios *mode)
+{
+       int i;
+       tcflag_t *bitsp;
 
-                       new_mode.c_cflag &= (~CIBAUD);
-                       if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
+       for (i = 0; i < NUM_control_info; ++i) {
+#if VMIN == VEOF
+               if (control_info[i].name == stty_min)
+                       break;
 #endif
-                               perror_on_device_and_die("%s: cannot perform all requested operations");
-               }
+               mode->c_cc[control_info[i].offset] = control_info[i].saneval;
        }
 
-       return EXIT_SUCCESS;
+       for (i = 0; i < NUM_mode_info; ++i) {
+               if (mode_info[i].flags & SANE_SET) {
+                       bitsp = mode_type_flag(mode_info[i].type, mode);
+                       *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
+                               | mode_info[i].bits;
+               } else if (mode_info[i].flags & SANE_UNSET) {
+                       bitsp = mode_type_flag(mode_info[i].type, mode);
+                       *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
+                               & ~mode_info[i].bits;
+               }
+       }
 }
 
 /* Save set_mode from #ifdef forest plague */
@@ -1091,26 +936,6 @@ static void set_mode(const struct mode_info *info, int reversed,
        }
 }
 
-static void set_control_char_or_die(const struct control_info *info,
-                       const char *arg, struct termios *mode)
-{
-       unsigned char value;
-
-       if (info->name == stty_min || info->name == stty_time)
-               value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
-       else if (arg[0] == '\0' || arg[1] == '\0')
-               value = arg[0];
-       else if (streq(arg, "^-") || streq(arg, "undef"))
-               value = _POSIX_VDISABLE;
-       else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
-               value = arg[1] & 0x1f; /* Non-letters get weird results */
-               if (arg[1] == '?')
-                       value = 127;
-       } else
-               value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
-       mode->c_cc[info->offset] = value;
-}
-
 static void display_changed(const struct termios *mode)
 {
        int i;
@@ -1156,152 +981,315 @@ static void display_changed(const struct termios *mode)
                        prev_type = mode_info[i].type;
                }
 
-               bitsp = mode_type_flag(mode_info[i].type, mode);
-               mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
-               if ((*bitsp & mask) == mode_info[i].bits) {
-                       if (mode_info[i].flags & SANE_UNSET) {
-                               wrapf("%s", mode_info[i].name);
-                       }
-               } else if ((mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)) {
-                       wrapf("-%s", mode_info[i].name);
+               bitsp = mode_type_flag(mode_info[i].type, mode);
+               mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
+               if ((*bitsp & mask) == mode_info[i].bits) {
+                       if (mode_info[i].flags & SANE_UNSET) {
+                               wrapf("%s", mode_info[i].name);
+                       }
+               } else if ((mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)) {
+                       wrapf("-%s", mode_info[i].name);
+               }
+       }
+       if (current_col) wrapf("\n");
+}
+
+static void set_control_char_or_die(const struct control_info *info,
+                       const char *arg, struct termios *mode)
+{
+       unsigned char value;
+
+       if (info->name == stty_min || info->name == stty_time)
+               value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
+       else if (arg[0] == '\0' || arg[1] == '\0')
+               value = arg[0];
+       else if (streq(arg, "^-") || streq(arg, "undef"))
+               value = _POSIX_VDISABLE;
+       else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
+               value = arg[1] & 0x1f; /* Non-letters get weird results */
+               if (arg[1] == '?')
+                       value = 127;
+       } else
+               value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
+       mode->c_cc[info->offset] = value;
+}
+
+int stty_main(int argc, char **argv)
+{
+       struct termios mode;
+       void (*output_func)(const struct termios *);
+       const char *file_name = NULL;
+       int require_set_attr;
+       int speed_was_set;
+       int verbose_output;
+       int recoverable_output;
+       int noargs;
+       int k;
+
+       output_func = display_changed;
+       noargs = 1;
+       speed_was_set = 0;
+       require_set_attr = 0;
+       verbose_output = 0;
+       recoverable_output = 0;
+
+       /* First pass: only parse/verify command line params */
+       k = 0;
+       while (argv[++k]) {
+               const struct mode_info *mp;
+               const struct control_info *cp;
+               const char *arg = argv[k];
+               const char *argnext = argv[k+1];
+               int param;
+
+               if (arg[0] == '-') {
+                       int i;
+                       mp = find_mode(arg+1);
+                       if (mp) {
+                               if (!(mp->flags & REV))
+                                       bb_error_msg_and_die("invalid argument '%s'", arg);
+                               noargs = 0;
+                               continue;
+                       }
+                       /* It is an option - parse it */
+                       i = 0;
+                       while (arg[++i]) {
+                               switch (arg[i]) {
+                               case 'a':
+                                       verbose_output = 1;
+                                       output_func = display_all;
+                                       break;
+                               case 'g':
+                                       recoverable_output = 1;
+                                       output_func = display_recoverable;
+                                       break;
+                               case 'F':
+                                       if (file_name)
+                                               bb_error_msg_and_die("only one device may be specified");
+                                       file_name = &arg[i+1]; /* "-Fdevice" ? */
+                                       if (!file_name[0]) { /* nope, "-F device" */
+                                               int p = k+1; /* argv[p] is argnext */
+                                               file_name = argnext;
+                                               if (!file_name)
+                                                       bb_error_msg_and_die(bb_msg_requires_arg, "-F");
+                                               /* remove -F param from arg[vc] */
+                                               --argc;
+                                               while (argv[p]) { argv[p] = argv[p+1]; ++p; }
+                                       }
+                                       goto end_option;
+                               default:
+                                       bb_error_msg_and_die("invalid argument '%s'", arg);
+                               }
+                       }
+end_option:
+                       continue;
+               }
+
+               mp = find_mode(arg);
+               if (mp) {
+                       noargs = 0;
+                       continue;
+               }
+
+               cp = find_control(arg);
+               if (cp) {
+                       if (!argnext)
+                               bb_error_msg_and_die(bb_msg_requires_arg, arg);
+                       /* called for the side effect of xfunc death only */
+                       set_control_char_or_die(cp, argnext, &mode);
+                       noargs = 0;
+                       ++k;
+                       continue;
+               }
+
+               param = find_param(arg);
+               if (param & param_need_arg) {
+                       if (!argnext)
+                               bb_error_msg_and_die(bb_msg_requires_arg, arg);
+                       ++k;
+               }
+
+               switch (param) {
+#ifdef HAVE_C_LINE
+               case param_line:
+# ifndef TIOCGWINSZ
+                       xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
+                       break;
+# endif /* else fall-through */
+#endif
+#ifdef TIOCGWINSZ
+               case param_rows:
+               case param_cols:
+                       xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
+                       break;
+               case param_size:
+#endif
+               case param_speed:
+                       break;
+               case param_ispeed:
+                       /* called for the side effect of xfunc death only */
+                       set_speed_or_die(input_speed, argnext, &mode);
+                       break;
+               case param_ospeed:
+                       /* called for the side effect of xfunc death only */
+                       set_speed_or_die(output_speed, argnext, &mode);
+                       break;
+               default:
+                       if (recover_mode(arg, &mode) == 1) break;
+                       if (string_to_baud_or_die(arg) != (speed_t) -1) break;
+                       bb_error_msg_and_die("invalid argument '%s'", arg);
+               }
+               noargs = 0;
+       }
+
+       /* Specifying both -a and -g is an error */
+       if (verbose_output && recoverable_output)
+               bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
+       /* Specifying -a or -g with non-options is an error */
+       if (!noargs && (verbose_output || recoverable_output))
+               bb_error_msg_and_die("modes may not be set when specifying an output style");
+
+       /* Now it is safe to start doing things */
+       if (file_name) {
+               int fd, fdflags;
+               device_name = file_name;
+               fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
+               if (fd != STDIN_FILENO) {
+                       dup2(fd, STDIN_FILENO);
+                       close(fd);
                }
+               fdflags = fcntl(STDIN_FILENO, F_GETFL);
+               if (fdflags == -1 || fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
+                       perror_on_device_and_die("%s: cannot reset non-blocking mode");
        }
-       if (current_col) wrapf("\n");
-}
 
-static void display_all(const struct termios *mode)
-{
-       int i;
-       tcflag_t *bitsp;
-       unsigned long mask;
-       int prev_type = control;
+       /* Initialize to all zeroes so there is no risk memcmp will report a
+          spurious difference in an uninitialized portion of the structure */
+       memset(&mode, 0, sizeof(mode));
+       if (tcgetattr(STDIN_FILENO, &mode))
+               perror_on_device_and_die("%s");
 
-       display_speed(mode, 1);
-       display_window_size(1);
-#ifdef HAVE_C_LINE
-       wrapf("line = %d;\n", mode->c_line);
-#else
-       wrapf("\n");
-#endif
+       if (verbose_output || recoverable_output || noargs) {
+               max_col = screen_columns_or_die();
+               output_func(&mode);
+               return EXIT_SUCCESS;
+       }
 
-       for (i = 0; control_info[i].name != stty_min; ++i) {
-               /* If swtch is the same as susp, don't print both */
-#if VSWTCH == VSUSP
-               if (control_info[i].name == stty_swtch)
+       /* Second pass: perform actions */
+       k = 0;
+       while (argv[++k]) {
+               const struct mode_info *mp;
+               const struct control_info *cp;
+               const char *arg = argv[k];
+               const char *argnext = argv[k+1];
+               int param;
+
+               if (arg[0] == '-') {
+                       mp = find_mode(arg+1);
+                       if (mp) {
+                               set_mode(mp, 1 /* reversed */, &mode);
+                       }
+                       /* It is an option - already parsed. Skip it */
                        continue;
-#endif
-               /* If eof uses the same slot as min, only print whichever applies */
-#if VEOF == VMIN
-               if ((mode->c_lflag & ICANON) == 0
-                       && (control_info[i].name == stty_eof
-                               || control_info[i].name == stty_eol)) continue;
-#endif
-               wrapf("%s = %s;", control_info[i].name,
-                         visible(mode->c_cc[control_info[i].offset]));
-       }
-#if VEOF == VMIN
-       if ((mode->c_lflag & ICANON) == 0)
-#endif
-               wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
-       if (current_col) wrapf("\n");
+               }
 
-       for (i = 0; i < NUM_mode_info; ++i) {
-               if (mode_info[i].flags & OMIT)
+               mp = find_mode(arg);
+               if (mp) {
+                       set_mode(mp, 0 /* non-reversed */, &mode);
                        continue;
-               if (mode_info[i].type != prev_type) {
-                       wrapf("\n");
-                       prev_type = mode_info[i].type;
                }
 
-               bitsp = mode_type_flag(mode_info[i].type, mode);
-               mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
-               if ((*bitsp & mask) == mode_info[i].bits)
-                       wrapf("%s", mode_info[i].name);
-               else if (mode_info[i].flags & REV)
-                       wrapf("-%s", mode_info[i].name);
-       }
-       if (current_col) wrapf("\n");
-}
+               cp = find_control(arg);
+               if (cp) {
+                       ++k;
+                       set_control_char_or_die(cp, argnext, &mode);
+                       continue;
+               }
 
-static void display_speed(const struct termios *mode, int fancy)
-{
-                            //01234567 8 9
-       const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
-       unsigned long ispeed, ospeed;
+               param = find_param(arg);
+               if (param & param_need_arg) {
+                       ++k;
+               }
 
-       ospeed = ispeed = cfgetispeed(mode);
-       if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
-               ispeed = ospeed;                /* in case ispeed was 0 */
-                        //0123 4 5 6 7 8 9
-               fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
+               switch (param) {
+#ifdef HAVE_C_LINE
+               case param_line:
+                       mode.c_line = xatoul_sfx(argnext, stty_suffixes);
+                       require_set_attr = 1;
+                       break;
+#endif
+#ifdef TIOCGWINSZ
+               case param_cols:
+                       set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
+                       break;
+               case param_size:
+                       display_window_size(0);
+                       break;
+               case param_rows:
+                       set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
+                       break;
+#endif
+               case param_speed:
+                       display_speed(&mode, 0);
+                       break;
+               case param_ispeed:
+                       set_speed_or_die(input_speed, argnext, &mode);
+                       speed_was_set = 1;
+                       require_set_attr = 1;
+                       break;
+               case param_ospeed:
+                       set_speed_or_die(output_speed, argnext, &mode);
+                       speed_was_set = 1;
+                       require_set_attr = 1;
+                       break;
+               default:
+                       if (recover_mode(arg, &mode) == 1)
+                               require_set_attr = 1;
+                       else /* true: if (string_to_baud_or_die(arg) != (speed_t) -1) */ {
+                               set_speed_or_die(both_speeds, arg, &mode);
+                               speed_was_set = 1;
+                               require_set_attr = 1;
+                       } /* else - impossible (caught in the first pass):
+                               bb_error_msg_and_die("invalid argument '%s'", arg); */
+               }
        }
-       if (fancy) fmt_str += 9;
-       wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
-}
-
-static void display_recoverable(const struct termios *mode)
-{
-       int i;
-       printf("%lx:%lx:%lx:%lx",
-                  (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
-                  (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
-       for (i = 0; i < NCCS; ++i)
-               printf(":%x", (unsigned int) mode->c_cc[i]);
-       putchar('\n');
-}
 
-static int recover_mode(const char *arg, struct termios *mode)
-{
-       int i, n;
-       unsigned int chr;
-       unsigned long iflag, oflag, cflag, lflag;
+       if (require_set_attr) {
+               struct termios new_mode;
 
-       /* Scan into temporaries since it is too much trouble to figure out
-          the right format for 'tcflag_t' */
-       if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
-                          &iflag, &oflag, &cflag, &lflag, &n) != 4)
-               return 0;
-       mode->c_iflag = iflag;
-       mode->c_oflag = oflag;
-       mode->c_cflag = cflag;
-       mode->c_lflag = lflag;
-       arg += n;
-       for (i = 0; i < NCCS; ++i) {
-               if (sscanf(arg, ":%x%n", &chr, &n) != 1)
-                       return 0;
-               mode->c_cc[i] = chr;
-               arg += n;
-       }
+               if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
+                       perror_on_device_and_die("%s");
 
-       /* Fail if there are too many fields */
-       if (*arg != '\0')
-               return 0;
+               /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
+                  it performs *any* of the requested operations.  This means it
+                  can report 'success' when it has actually failed to perform
+                  some proper subset of the requested operations.  To detect
+                  this partial failure, get the current terminal attributes and
+                  compare them to the requested ones */
 
-       return 1;
-}
+               /* Initialize to all zeroes so there is no risk memcmp will report a
+                  spurious difference in an uninitialized portion of the structure */
+               memset(&new_mode, 0, sizeof(new_mode));
+               if (tcgetattr(STDIN_FILENO, &new_mode))
+                       perror_on_device_and_die("%s");
 
-static void sane_mode(struct termios *mode)
-{
-       int i;
-       tcflag_t *bitsp;
+               if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
+#ifdef CIBAUD
+                       /* SunOS 4.1.3 (at least) has the problem that after this sequence,
+                          tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
+                          sometimes (m1 != m2).  The only difference is in the four bits
+                          of the c_cflag field corresponding to the baud rate.  To save
+                          Sun users a little confusion, don't report an error if this
+                          happens.  But suppress the error only if we haven't tried to
+                          set the baud rate explicitly -- otherwise we'd never give an
+                          error for a true failure to set the baud rate */
 
-       for (i = 0; i < NUM_control_info; ++i) {
-#if VMIN == VEOF
-               if (control_info[i].name == stty_min)
-                       break;
+                       new_mode.c_cflag &= (~CIBAUD);
+                       if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
 #endif
-               mode->c_cc[control_info[i].offset] = control_info[i].saneval;
-       }
-
-       for (i = 0; i < NUM_mode_info; ++i) {
-               if (mode_info[i].flags & SANE_SET) {
-                       bitsp = mode_type_flag(mode_info[i].type, mode);
-                       *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
-                               | mode_info[i].bits;
-               } else if (mode_info[i].flags & SANE_UNSET) {
-                       bitsp = mode_type_flag(mode_info[i].type, mode);
-                       *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
-                               & ~mode_info[i].bits;
+                               perror_on_device_and_die("%s: cannot perform all requested operations");
                }
        }
+
+       return EXIT_SUCCESS;
 }