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
21 changes: 21 additions & 0 deletions src/uu/stty/src/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
// spell-checker:ignore isig icanon iexten echoe crterase echok echonl noflsh xcase tostop echoprt prterase echoctl ctlecho echoke crtkill flusho extproc
// spell-checker:ignore lnext rprnt susp swtch vdiscard veof veol verase vintr vkill vlnext vquit vreprint vstart vstop vsusp vswtc vwerase werase
// spell-checker:ignore sigquit sigtstp
// spell-checker:ignore cbreak decctlq evenp litout oddp

use crate::Flag;

Expand Down Expand Up @@ -365,3 +366,23 @@ pub const CONTROL_CHARS: &[(&str, S)] = &[
// Discards the current line.
("discard", S::VDISCARD),
];

/// This constant lists all possible combination settings, using a bool to represent if the setting is negatable
pub const COMBINATION_SETTINGS: &[(&str, bool)] = &[
("LCASE", true),
("lcase", true),
("cbreak", true),
("cooked", true),
("crt", false),
("dec", false),
("decctlq", true),
("ek", false),
("evenp", true),
("litout", true),
("nl", true),
("oddp", true),
("parity", true),
("pass8", true),
("raw", true),
("sane", false),
];
160 changes: 148 additions & 12 deletions src/uu/stty/src/stty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,23 @@
// file that was distributed with this source code.

// spell-checker:ignore clocal erange tcgetattr tcsetattr tcsanow tiocgwinsz tiocswinsz cfgetospeed cfsetospeed ushort vmin vtime cflag lflag ispeed ospeed
// spell-checker:ignore tcsadrain
// spell-checker:ignore parenb parodd cmspar hupcl cstopb cread clocal crtscts CSIZE
// spell-checker:ignore ignbrk brkint ignpar parmrk inpck istrip inlcr igncr icrnl ixoff ixon iuclc ixany imaxbel iutf
// spell-checker:ignore opost olcuc ocrnl onlcr onocr onlret ofdel nldly crdly tabdly bsdly vtdly ffdly ofill
// spell-checker:ignore isig icanon iexten echoe crterase echok echonl noflsh xcase tostop echoprt prterase echoctl ctlecho echoke crtkill flusho extproc
// spell-checker:ignore lnext rprnt susp swtch vdiscard veof veol verase vintr vkill vlnext vquit vreprint vstart vstop vsusp vswtc vwerase werase
// spell-checker:ignore sigquit sigtstp
// spell-checker:ignore cbreak decctlq evenp litout oddp tcsadrain

mod flags;

use crate::flags::AllFlags;
use crate::flags::COMBINATION_SETTINGS;
use clap::{Arg, ArgAction, ArgMatches, Command};
use nix::libc::{O_NONBLOCK, TIOCGWINSZ, TIOCSWINSZ, c_ushort};
use nix::sys::termios::{
ControlFlags, InputFlags, LocalFlags, OutputFlags, SetArg, SpecialCharacterIndices, Termios,
cfgetospeed, cfsetospeed, tcgetattr, tcsetattr,
ControlFlags, InputFlags, LocalFlags, OutputFlags, SetArg, SpecialCharacterIndices as S,
Termios, cfgetospeed, cfsetospeed, tcgetattr, tcsetattr,
};
use nix::{ioctl_read_bad, ioctl_write_ptr_bad};
use std::collections::HashMap;
Expand Down Expand Up @@ -106,6 +113,7 @@
Stdout(Stdout),
}

#[derive(Debug)]
enum ControlCharMappingError {
IntOutOfRange(String),
MultipleChars(String),
Expand All @@ -123,7 +131,7 @@

enum ArgOptions<'a> {
Flags(AllFlags<'a>),
Mapping((SpecialCharacterIndices, u8)),
Mapping((S, u8)),
Special(SpecialSetting),
Print(PrintSetting),
}
Expand Down Expand Up @@ -323,8 +331,7 @@
match args_iter.next() {
Some(min) => match parse_u8_or_err(min) {
Ok(n) => {
valid_args
.push(ArgOptions::Mapping((SpecialCharacterIndices::VMIN, n)));
valid_args.push(ArgOptions::Mapping((S::VMIN, n)));

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

View check run for this annotation

Codecov / codecov/patch

src/uu/stty/src/stty.rs#L334

Added line #L334 was not covered by tests
}
Err(e) => return Err(USimpleError::new(1, e)),
},
Expand All @@ -341,8 +348,7 @@
} else if arg == "time" {
match args_iter.next() {
Some(time) => match parse_u8_or_err(time) {
Ok(n) => valid_args
.push(ArgOptions::Mapping((SpecialCharacterIndices::VTIME, n))),
Ok(n) => valid_args.push(ArgOptions::Mapping((S::VTIME, n))),

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

View check run for this annotation

Codecov / codecov/patch

src/uu/stty/src/stty.rs#L351

Added line #L351 was not covered by tests
Err(e) => return Err(USimpleError::new(1, e)),
},
None => {
Expand Down Expand Up @@ -377,6 +383,9 @@
));
}
valid_args.push(flag.into());
// combination setting
} else if let Some(combo) = string_to_combo(arg) {
valid_args.append(&mut combo_to_flags(combo));

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

View check run for this annotation

Codecov / codecov/patch

src/uu/stty/src/stty.rs#L388

Added line #L388 was not covered by tests
} else if arg == "rows" {
if let Some(rows) = args_iter.next() {
if let Some(n) = parse_rows_cols(rows) {
Expand Down Expand Up @@ -581,7 +590,7 @@
Ok(())
}

fn cc_to_index(option: &str) -> Option<SpecialCharacterIndices> {
fn cc_to_index(option: &str) -> Option<S> {
for cc in CONTROL_CHARS {
if option == cc.0 {
return Some(cc.1);
Expand All @@ -590,6 +599,15 @@
None
}

fn string_to_combo(arg: &str) -> Option<&str> {
let is_negated = arg.starts_with('-');
let name = arg.trim_start_matches('-');
COMBINATION_SETTINGS
.iter()
.find(|&&(combo_name, is_negatable)| name == combo_name && (!is_negated || is_negatable))
.map(|_| arg)
}

fn string_to_baud(arg: &str) -> Option<AllFlags> {
// BSDs use a u32 for the baud rate, so any decimal number applies.
#[cfg(any(
Expand Down Expand Up @@ -693,11 +711,11 @@
HashMap::from([
(
"min".to_string(),
termios.control_chars[SpecialCharacterIndices::VMIN as usize].to_string()
termios.control_chars[S::VMIN as usize].to_string()

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

View check run for this annotation

Codecov / codecov/patch

src/uu/stty/src/stty.rs#L714

Added line #L714 was not covered by tests
),
(
"time".to_string(),
termios.control_chars[SpecialCharacterIndices::VTIME as usize].to_string()
termios.control_chars[S::VTIME as usize].to_string()

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

View check run for this annotation

Codecov / codecov/patch

src/uu/stty/src/stty.rs#L718

Added line #L718 was not covered by tests
)
])
)
Expand Down Expand Up @@ -812,7 +830,7 @@
}
}

fn apply_char_mapping(termios: &mut Termios, mapping: &(SpecialCharacterIndices, u8)) {
fn apply_char_mapping(termios: &mut Termios, mapping: &(S, u8)) {

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

View check run for this annotation

Codecov / codecov/patch

src/uu/stty/src/stty.rs#L833

Added line #L833 was not covered by tests
termios.control_chars[mapping.0 as usize] = mapping.1;
}

Expand Down Expand Up @@ -889,6 +907,124 @@
}
}

// decomposes a combination argument into a vec of corresponding flags
fn combo_to_flags(combo: &str) -> Vec<ArgOptions> {
let mut flags = Vec::new();
let mut ccs = Vec::new();
match combo {
"lcase" | "LCASE" => {
flags = vec!["xcase", "iuclc", "olcuc"];
}
"-lcase" | "-LCASE" => {
flags = vec!["-xcase", "-iuclc", "-olcuc"];
}
Comment on lines +918 to +920
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't there be the same flags as for lcase/LCASE? From the help:

[-]lcase      same as xcase iuclc olcuc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with GNU stty, '-lcase'/'-LCASE' yields '-xcase -iuclc -olcuc'

"cbreak" => {
flags = vec!["-icanon"];
}
"-cbreak" => {
flags = vec!["icanon"];
}
"cooked" | "-raw" => {
flags = vec![
"brkint", "ignpar", "istrip", "icrnl", "ixon", "opost", "isig", "icanon",
];
ccs = vec![(S::VEOF, "^D"), (S::VEOL, "")];
}
"crt" => {
flags = vec!["echoe", "echoctl", "echoke"];
}
"dec" => {
flags = vec!["echoe", "echoctl", "echoke", "-ixany"];
ccs = vec![(S::VINTR, "^C"), (S::VERASE, "^?"), (S::VKILL, "^U")];
}
"decctlq" => {
flags = vec!["ixany"];
}
"-decctlq" => {
flags = vec!["-ixany"];
}
"ek" => {
ccs = vec![(S::VERASE, "^?"), (S::VKILL, "^U")];
}
"evenp" | "parity" => {
flags = vec!["parenb", "-parodd", "cs7"];
}
"-evenp" | "-parity" => {
flags = vec!["-parenb", "cs8"];
}
"litout" => {
flags = vec!["-parenb", "-istrip", "-opost", "cs8"];
}
"-litout" => {
flags = vec!["parenb", "istrip", "opost", "cs7"];
}
"nl" => {
flags = vec!["-icrnl", "-onlcr"];
}
"-nl" => {
flags = vec!["icrnl", "-inlcr", "-igncr", "onlcr", "-ocrnl", "-onlret"];
}
"oddp" => {
flags = vec!["parenb", "parodd", "cs7"];
}
"-oddp" => {
flags = vec!["-parenb", "cs8"];
}
"pass8" => {
flags = vec!["-parenb", "-istrip", "cs8"];
}
"-pass8" => {
flags = vec!["parenb", "istrip", "cs7"];
}
"raw" | "-cooked" => {
flags = vec![
"-ignbrk", "-brkint", "-ignpar", "-parmrk", "-inpck", "-istrip", "-inlcr",
"-igncr", "-icrnl", "-ixon", "-ixoff", "-icanon", "-opost", "-isig", "-iuclc",
"-xcase", "-ixany", "-imaxbel",
];
ccs = vec![(S::VMIN, "1"), (S::VTIME, "0")];
}
"sane" => {
flags = vec![
"cread", "-ignbrk", "brkint", "-inlcr", "-igncr", "icrnl", "icanon", "iexten",
"echo", "echoe", "echok", "-echonl", "-noflsh", "-ixoff", "-iutf8", "-iuclc",
"-xcase", "-ixany", "imaxbel", "-olcuc", "-ocrnl", "opost", "-ofill", "onlcr",
"-onocr", "-onlret", "nl0", "cr0", "tab0", "bs0", "vt0", "ff0", "isig", "-tostop",
"-ofdel", "-echoprt", "echoctl", "echoke", "-extproc", "-flusho",
];
ccs = vec![
(S::VINTR, "^C"),
(S::VQUIT, "^\\"),
(S::VERASE, "^?"),
(S::VKILL, "^U"),
(S::VEOF, "^D"),
(S::VEOL, ""),
(S::VEOL2, ""),
#[cfg(target_os = "linux")]
(S::VSWTC, ""),
(S::VSTART, "^Q"),
(S::VSTOP, "^S"),
(S::VSUSP, "^Z"),
(S::VREPRINT, "^R"),
(S::VWERASE, "^W"),
(S::VLNEXT, "^V"),
(S::VDISCARD, "^O"),
];
}
_ => unreachable!("invalid combination setting: must have been caught earlier"),

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

View check run for this annotation

Codecov / codecov/patch

src/uu/stty/src/stty.rs#L911-L1014

Added lines #L911 - L1014 were not covered by tests
}
let mut flags = flags
.iter()
.filter_map(|f| string_to_flag(f).map(ArgOptions::Flags))
.collect::<Vec<ArgOptions>>();
let mut ccs = ccs
.iter()
.map(|cc| ArgOptions::Mapping((cc.0, string_to_control_char(cc.1).unwrap())))
.collect::<Vec<ArgOptions>>();
flags.append(&mut ccs);
flags
}

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

View check run for this annotation

Codecov / codecov/patch

src/uu/stty/src/stty.rs#L1016-L1026

Added lines #L1016 - L1026 were not covered by tests

pub fn uu_app() -> Command {
Command::new(uucore::util_name())
.version(uucore::crate_version!())
Expand Down
16 changes: 16 additions & 0 deletions tests/by-util/test_stty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,3 +293,19 @@ fn min_and_time() {
.fails()
.stderr_contains("invalid integer argument: '256': Value too large for defined data type");
}

#[test]
fn non_negatable_combo() {
new_ucmd!()
.args(&["-dec"])
.fails()
.stderr_contains("invalid argument '-dec'");
new_ucmd!()
.args(&["-crt"])
.fails()
.stderr_contains("invalid argument '-crt'");
new_ucmd!()
.args(&["-ek"])
.fails()
.stderr_contains("invalid argument '-ek'");
}
Loading