1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-27 11:07:44 +00:00

stty: add line setting

This commit is contained in:
Will Shuttleworth 2025-06-30 20:54:03 -04:00
parent 8a6c576fb7
commit ff11406c7a
2 changed files with 65 additions and 2 deletions

View file

@ -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<u8, String> {
arg.parse::<u8>().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<u16> {
@ -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(())

View file

@ -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'");
}