diff --git a/src/uu/stty/src/stty.rs b/src/uu/stty/src/stty.rs index fdeee252df3..1f3df4e39f5 100644 --- a/src/uu/stty/src/stty.rs +++ b/src/uu/stty/src/stty.rs @@ -564,19 +564,58 @@ fn string_to_combo(arg: &str) -> Option<&str> { } fn string_to_baud(arg: &str) -> Option> { - // BSDs use a u32 for the baud rate, so any decimal number applies. - #[cfg(any( - target_os = "freebsd", - target_os = "dragonfly", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd" - ))] - if let Ok(n) = arg.parse::() { - return Some(AllFlags::Baud(n)); + // Normalize the input: trim whitespace and leading '+' + let normalized = arg.trim().trim_start_matches('+'); + + // Try to parse as a floating point number for rounding + if let Ok(f) = normalized.parse::() { + let rounded = f.round() as u32; + + // BSDs use a u32 for the baud rate, so any decimal number applies. + #[cfg(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] + return Some(AllFlags::Baud(rounded)); + + // On other platforms, find the closest valid baud rate + #[cfg(not(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + )))] + { + let rounded_str = rounded.to_string(); + // First try exact match + for (text, baud_rate) in BAUD_RATES { + if *text == rounded_str { + return Some(AllFlags::Baud(*baud_rate)); + } + } + // If no exact match, find closest baud rate + let mut closest: Option<(u32, nix::sys::termios::BaudRate)> = None; + for (text, baud_rate) in BAUD_RATES { + if let Ok(rate_val) = text.parse::() { + let diff = rate_val.abs_diff(rounded); + if closest.is_none() || diff < closest.unwrap().0 { + closest = Some((diff, *baud_rate)); + } + } + } + if let Some((_, baud_rate)) = closest { + return Some(AllFlags::Baud(baud_rate)); + } + } } - + + // Fallback: try exact string match for non-numeric baud rate names #[cfg(not(any( target_os = "freebsd", target_os = "dragonfly", @@ -586,10 +625,11 @@ fn string_to_baud(arg: &str) -> Option> { target_os = "openbsd" )))] for (text, baud_rate) in BAUD_RATES { - if *text == arg { + if *text == normalized { return Some(AllFlags::Baud(*baud_rate)); } } + None } @@ -1082,3 +1122,59 @@ impl TermiosFlag for LocalFlags { termios.local_flags.set(*self, val); } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_string_to_baud_exact_match() { + assert!(string_to_baud("9600").is_some()); + assert!(string_to_baud("115200").is_some()); + } + + #[test] + fn test_string_to_baud_with_whitespace() { + assert!(string_to_baud(" 9600 ").is_some()); + assert!(string_to_baud("\t115200\n").is_some()); + } + + #[test] + fn test_string_to_baud_with_plus_sign() { + assert!(string_to_baud("+9600").is_some()); + assert!(string_to_baud(" +115200 ").is_some()); + } + + #[test] + fn test_string_to_baud_with_decimal() { + assert!(string_to_baud("9600.0").is_some()); + assert!(string_to_baud("9600.4").is_some()); + } + + #[test] + fn test_string_to_baud_rounding() { + assert!(string_to_baud("9600.5").is_some()); + assert!(string_to_baud("9599.6").is_some()); + } + + #[cfg(not(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + )))] + #[test] + fn test_string_to_baud_closest_match() { + assert!(string_to_baud("9601").is_some()); + assert!(string_to_baud("115000").is_some()); + } + + #[test] + fn test_string_to_baud_invalid() { + assert!(string_to_baud("invalid").is_none()); + assert!(string_to_baud("").is_none()); + assert!(string_to_baud("abc123").is_none()); + } +} diff --git a/util/build-gnu.sh b/util/build-gnu.sh index f3596c411ea..f7a6a983c28 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -130,6 +130,10 @@ for binary in $(./build-aux/gen-lists-of-programs.sh --list-progs); do } done +# Always update the PATH to test the uutils coreutils instead of the GNU coreutils +# This ensures the correct path is used even if the repository was moved or rebuilt in a different location +sed -i "s/^[[:blank:]]*PATH=.*/ PATH='${UU_BUILD_DIR//\//\\/}\$(PATH_SEPARATOR)'\"\$\$PATH\" \\\/" tests/local.mk + if test -f gnu-built; then echo "GNU build already found. Skip" echo "'rm -f $(pwd)/gnu-built' to force the build" @@ -137,8 +141,6 @@ if test -f gnu-built; then else # Disable useless checks sed -i 's|check-texinfo: $(syntax_checks)|check-texinfo:|' doc/local.mk - # Change the PATH to test the uutils coreutils instead of the GNU coreutils - sed -i "s/^[[:blank:]]*PATH=.*/ PATH='${UU_BUILD_DIR//\//\\/}\$(PATH_SEPARATOR)'\"\$\$PATH\" \\\/" tests/local.mk ./bootstrap --skip-po ./configure --quiet --disable-gcc-warnings --disable-nls --disable-dependency-tracking --disable-bold-man-page-references \ "$([ "${SELINUX_ENABLED}" = 1 ] && echo --with-selinux || echo --without-selinux)"