Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 109 additions & 13 deletions src/uu/stty/src/stty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -564,19 +564,58 @@ fn string_to_combo(arg: &str) -> Option<&str> {
}

fn string_to_baud(arg: &str) -> Option<AllFlags<'_>> {
// 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::<u32>() {
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::<f64>() {
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::<u32>() {
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",
Expand All @@ -586,10 +625,11 @@ fn string_to_baud(arg: &str) -> Option<AllFlags<'_>> {
target_os = "openbsd"
)))]
for (text, baud_rate) in BAUD_RATES {
if *text == arg {
if *text == normalized {
return Some(AllFlags::Baud(*baud_rate));
}
}

None
}

Expand Down Expand Up @@ -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());
}
}
6 changes: 4 additions & 2 deletions util/build-gnu.sh
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,17 @@ 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"
echo "Note: the customization of the tests will still happen"
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)"
Expand Down
Loading