stty: rearrange functions, avoiding the need in forward declarations. No other code chages.
No binary (build failed)
—
(log yok)
| @@ -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; |
| } | |