diff --git a/src/uu/stty/src/stty.rs b/src/uu/stty/src/stty.rs index 52fc11bcb..b811e084e 100644 --- a/src/uu/stty/src/stty.rs +++ b/src/uu/stty/src/stty.rs @@ -19,6 +19,7 @@ use nix::{ioctl_read_bad, ioctl_write_ptr_bad}; use std::collections::HashMap; use std::fs::File; use std::io::{self, Stdout, stdout}; +use std::num::IntErrorKind; use std::os::fd::{AsFd, BorrowedFd}; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::{AsRawFd, RawFd}; @@ -113,6 +114,7 @@ enum ControlCharMappingError { enum SpecialSetting { Rows(u16), Cols(u16), + Line(u8), } enum PrintSetting { @@ -301,6 +303,22 @@ fn stty(opts: &Options) -> UResult<()> { )); } } + } else if *arg == "line" { + match args_iter.next() { + Some(line) => match parse_u8_or_err(line) { + Ok(n) => valid_args.push(ArgOptions::Special(SpecialSetting::Line(n))), + Err(e) => return Err(USimpleError::new(1, e)), + }, + None => { + return Err(USimpleError::new( + 1, + get_message_with_args( + "stty-error-missing-argument", + HashMap::from([("arg".to_string(), arg.to_string())]), + ), + )); + } + } // baud rate setting } else if let Some(baud_flag) = string_to_baud(arg) { valid_args.push(ArgOptions::Flags(baud_flag)); @@ -394,7 +412,7 @@ fn stty(opts: &Options) -> UResult<()> { ArgOptions::Mapping(mapping) => apply_char_mapping(&mut termios, mapping), ArgOptions::Flags(flag) => apply_setting(&mut termios, flag), ArgOptions::Special(setting) => { - apply_special_setting(setting, opts.file.as_raw_fd())?; + apply_special_setting(&mut termios, setting, opts.file.as_raw_fd())?; } ArgOptions::Print(setting) => { print_special_setting(setting, opts.file.as_raw_fd())?; @@ -411,6 +429,21 @@ fn stty(opts: &Options) -> UResult<()> { Ok(()) } +// GNU uses different error messages if values overflow or underflow a u8 +// this function returns the appropriate error message in the case of overflow or underflow, or u8 on success +fn parse_u8_or_err(arg: &str) -> Result { + arg.parse::().map_err(|e| match e.kind() { + IntErrorKind::PosOverflow => get_message_with_args( + "stty-error-invalid-integer-argument-value-too-large", + HashMap::from([("value".to_string(), format!("'{}'", arg))]), + ), + _ => get_message_with_args( + "stty-error-invalid-integer-argument", + HashMap::from([("value".to_string(), format!("'{}'", arg))]), + ), + }) +} + // GNU uses an unsigned 32 bit integer for row/col sizes, but then wraps around 16 bits // this function returns Some(n), where n is a u16 row/col size, or None if the string arg cannot be parsed as a u32 fn parse_rows_cols(arg: &str) -> Option { @@ -747,12 +780,23 @@ fn apply_char_mapping(termios: &mut Termios, mapping: &(SpecialCharacterIndices, termios.control_chars[mapping.0 as usize] = mapping.1; } -fn apply_special_setting(setting: &SpecialSetting, fd: i32) -> nix::Result<()> { +fn apply_special_setting( + _termios: &mut Termios, + setting: &SpecialSetting, + fd: i32, +) -> nix::Result<()> { let mut size = TermSize::default(); unsafe { tiocgwinsz(fd, &raw mut size)? }; match setting { SpecialSetting::Rows(n) => size.rows = *n, SpecialSetting::Cols(n) => size.columns = *n, + SpecialSetting::Line(_n) => { + // nix only defines Termios's `line_discipline` field on these platforms + #[cfg(any(target_os = "linux", target_os = "android"))] + { + _termios.line_discipline = *_n; + } + } } unsafe { tiocswinsz(fd, &raw mut size)? }; Ok(()) diff --git a/tests/by-util/test_stty.rs b/tests/by-util/test_stty.rs index 0fefec992..9a2d5a64c 100644 --- a/tests/by-util/test_stty.rs +++ b/tests/by-util/test_stty.rs @@ -241,3 +241,22 @@ fn row_column_sizes() { .fails() .stderr_contains("missing argument to 'rows'"); } + +#[test] +#[cfg(any(target_os = "linux", target_os = "android"))] +fn line() { + new_ucmd!() + .args(&["line"]) + .fails() + .stderr_contains("missing argument to 'line'"); + + new_ucmd!() + .args(&["line", "-1"]) + .fails() + .stderr_contains("invalid integer argument: '-1'"); + + new_ucmd!() + .args(&["line", "256"]) + .fails() + .stderr_contains("invalid integer argument: '256'"); +}