Skip to content
Merged
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
78 changes: 77 additions & 1 deletion src/uu/stty/src/stty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,20 @@
MultipleChars,
}

enum SpecialSetting {
Rows(u16),
Cols(u16),
}

enum PrintSetting {
Size,
}

enum ArgOptions<'a> {
Flags(AllFlags<'a>),
Mapping((SpecialCharacterIndices, u8)),
Special(SpecialSetting),
Print(PrintSetting),
}

impl<'a> From<AllFlags<'a>> for ArgOptions<'a> {
Expand Down Expand Up @@ -280,7 +291,35 @@
return Err(USimpleError::new(1, format!("invalid argument '{arg}'")));
}
valid_args.push(flag.into());
// not a valid control char or flag
} else if *arg == "rows" {
if let Some(rows) = args_iter.next() {
if let Some(n) = parse_rows_cols(rows) {
valid_args.push(ArgOptions::Special(SpecialSetting::Rows(n)));
} else {

Check warning on line 298 in src/uu/stty/src/stty.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/stty/src/stty.rs#L297-L298

Added lines #L297 - L298 were not covered by tests
return Err(USimpleError::new(
1,
format!("invalid integer argument: '{rows}'"),
));
}
} else {
return Err(USimpleError::new(1, format!("missing argument to '{arg}'")));
}
} else if *arg == "columns" || *arg == "cols" {
if let Some(cols) = args_iter.next() {
if let Some(n) = parse_rows_cols(cols) {
valid_args.push(ArgOptions::Special(SpecialSetting::Cols(n)));
} else {

Check warning on line 311 in src/uu/stty/src/stty.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/stty/src/stty.rs#L310-L311

Added lines #L310 - L311 were not covered by tests
return Err(USimpleError::new(
1,
format!("invalid integer argument: '{cols}'"),
));
}
} else {
return Err(USimpleError::new(1, format!("missing argument to '{arg}'")));
}
} else if *arg == "size" {
valid_args.push(ArgOptions::Print(PrintSetting::Size));
// not a valid option

Check warning on line 322 in src/uu/stty/src/stty.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/stty/src/stty.rs#L321-L322

Added lines #L321 - L322 were not covered by tests
} else {
return Err(USimpleError::new(1, format!("invalid argument '{arg}'")));
}
Expand All @@ -294,6 +333,12 @@
match arg {
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())?;

Check warning on line 337 in src/uu/stty/src/stty.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/stty/src/stty.rs#L336-L337

Added lines #L336 - L337 were not covered by tests
}
ArgOptions::Print(setting) => {
print_special_setting(setting, opts.file.as_raw_fd())?;

Check warning on line 340 in src/uu/stty/src/stty.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/stty/src/stty.rs#L339-L340

Added lines #L339 - L340 were not covered by tests
}
}
}
tcsetattr(
Expand All @@ -310,10 +355,30 @@
Ok(())
}

// 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> {
if let Ok(n) = arg.parse::<u32>() {
return Some((n % (u16::MAX as u32 + 1)) as u16);

Check warning on line 362 in src/uu/stty/src/stty.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/stty/src/stty.rs#L362

Added line #L362 was not covered by tests
}
None
}

fn check_flag_group<T>(flag: &Flag<T>, remove: bool) -> bool {
remove && flag.group.is_some()
}

fn print_special_setting(setting: &PrintSetting, fd: i32) -> nix::Result<()> {
match setting {

Check warning on line 372 in src/uu/stty/src/stty.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/stty/src/stty.rs#L371-L372

Added lines #L371 - L372 were not covered by tests
PrintSetting::Size => {
let mut size = TermSize::default();
unsafe { tiocgwinsz(fd, &raw mut size)? };
println!("{} {}", size.rows, size.columns);

Check warning on line 376 in src/uu/stty/src/stty.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/stty/src/stty.rs#L374-L376

Added lines #L374 - L376 were not covered by tests
}
}
Ok(())
}

Check warning on line 380 in src/uu/stty/src/stty.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/stty/src/stty.rs#L379-L380

Added lines #L379 - L380 were not covered by tests

fn print_terminal_size(termios: &Termios, opts: &Options) -> nix::Result<()> {
let speed = cfgetospeed(termios);

Expand Down Expand Up @@ -588,6 +653,17 @@
termios.control_chars[mapping.0 as usize] = mapping.1;
}

fn apply_special_setting(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,

Check warning on line 661 in src/uu/stty/src/stty.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/stty/src/stty.rs#L656-L661

Added lines #L656 - L661 were not covered by tests
}
unsafe { tiocswinsz(fd, &raw mut size)? };
Ok(())
}

Check warning on line 665 in src/uu/stty/src/stty.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/stty/src/stty.rs#L663-L665

Added lines #L663 - L665 were not covered by tests

// GNU stty defines some valid values for the control character mappings
// 1. Standard character, can be a a single char (ie 'C') or hat notation (ie '^C')
// 2. Integer
Expand Down
42 changes: 42 additions & 0 deletions tests/by-util/test_stty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ fn all_and_setting() {
.stderr_contains("when specifying an output style, modes may not be set");
}

#[test]
fn all_and_print_setting() {
new_ucmd!()
.args(&["--all", "size"])
.fails()
.stderr_contains("when specifying an output style, modes may not be set");
}

#[test]
fn save_and_all() {
new_ucmd!()
Expand Down Expand Up @@ -201,3 +209,37 @@ fn set_mapping() {
.succeeds()
.stdout_contains("intr = ^C");
}

#[test]
fn row_column_sizes() {
new_ucmd!()
.args(&["rows", "-1"])
.fails()
.stderr_contains("invalid integer argument: '-1'");

new_ucmd!()
.args(&["columns", "-1"])
.fails()
.stderr_contains("invalid integer argument: '-1'");

// overflow the u32 used for row/col counts
new_ucmd!()
.args(&["cols", "4294967296"])
.fails()
.stderr_contains("invalid integer argument: '4294967296'");

new_ucmd!()
.args(&["rows", ""])
.fails()
.stderr_contains("invalid integer argument: ''");

new_ucmd!()
.args(&["columns"])
.fails()
.stderr_contains("missing argument to 'columns'");

new_ucmd!()
.args(&["rows"])
.fails()
.stderr_contains("missing argument to 'rows'");
}
Loading