stty: refactor arg processing loop

This commit is contained in:
Will Shuttleworth 2025-07-20 15:39:00 -04:00
parent 4bbbb972ad
commit f836889ff2

View file

@ -30,7 +30,7 @@ use std::num::IntErrorKind;
use std::os::fd::{AsFd, BorrowedFd};
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::{AsRawFd, RawFd};
use uucore::error::{UResult, USimpleError};
use uucore::error::{UError, UResult, USimpleError};
use uucore::format_usage;
use uucore::locale::{get_message, get_message_with_args};
@ -270,37 +270,9 @@ fn stty(opts: &Options) -> UResult<()> {
if let Some(args) = &opts.settings {
let mut args_iter = args.iter();
// iterate over args: skip to next arg if current one is a control char
while let Some(&arg) = args_iter.next() {
// control char
if let Some(char_index) = cc_to_index(arg) {
if let Some(mapping) = args_iter.next() {
let cc_mapping = string_to_control_char(mapping).map_err(|e| {
let message = match e {
ControlCharMappingError::IntOutOfRange(val) => get_message_with_args(
"stty-error-invalid-integer-argument-value-too-large",
HashMap::from([("value".to_string(), format!("'{val}'"))]),
),
ControlCharMappingError::MultipleChars(val) => get_message_with_args(
"stty-error-invalid-integer-argument",
HashMap::from([("value".to_string(), format!("'{val}'"))]),
),
};
USimpleError::new(1, message)
})?;
valid_args.push(ArgOptions::Mapping((char_index, cc_mapping)));
} else {
return Err(USimpleError::new(
1,
get_message_with_args(
"stty-error-missing-argument",
HashMap::from([("arg".to_string(), (*arg).to_string())]),
),
));
}
// ispeed/ospeed baud rate setting
} else if arg == "ispeed" || arg == "ospeed" {
match args_iter.next() {
match arg {
"ispeed" | "ospeed" => match args_iter.next() {
Some(speed) => {
if let Some(baud_flag) = string_to_baud(speed) {
valid_args.push(ArgOptions::Flags(baud_flag));
@ -318,33 +290,19 @@ fn stty(opts: &Options) -> UResult<()> {
}
}
None => {
return Err(USimpleError::new(
1,
get_message_with_args(
"stty-error-missing-argument",
HashMap::from([("arg".to_string(), (*arg).to_string())]),
),
));
return missing_arg(arg);
}
}
} else if arg == "line" {
match args_iter.next() {
},
"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())]),
),
));
return missing_arg(arg);
}
}
} else if arg == "min" {
match args_iter.next() {
},
"min" => match args_iter.next() {
Some(min) => match parse_u8_or_err(min) {
Ok(n) => {
valid_args.push(ArgOptions::Mapping((S::VMIN, n)));
@ -352,115 +310,105 @@ fn stty(opts: &Options) -> UResult<()> {
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())]),
),
));
return missing_arg(arg);
}
}
} else if arg == "time" {
match args_iter.next() {
},
"time" => match args_iter.next() {
Some(time) => match parse_u8_or_err(time) {
Ok(n) => valid_args.push(ArgOptions::Mapping((S::VTIME, 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())]),
),
));
return missing_arg(arg);
}
}
// baud rate setting
} else if let Some(baud_flag) = string_to_baud(arg) {
valid_args.push(ArgOptions::Flags(baud_flag));
// non control char flag
} else if let Some(flag) = string_to_flag(arg) {
let remove_group = match flag {
AllFlags::Baud(_) => false,
AllFlags::ControlFlags((flag, remove)) => check_flag_group(flag, remove),
AllFlags::InputFlags((flag, remove)) => check_flag_group(flag, remove),
AllFlags::LocalFlags((flag, remove)) => check_flag_group(flag, remove),
AllFlags::OutputFlags((flag, remove)) => check_flag_group(flag, remove),
};
if remove_group {
return Err(USimpleError::new(
1,
get_message_with_args(
"stty-error-invalid-argument",
HashMap::from([("arg".to_string(), (*arg).to_string())]),
),
));
}
valid_args.push(flag.into());
// combination setting
} else if let Some(combo) = string_to_combo(arg) {
valid_args.append(&mut combo_to_flags(combo));
} 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)));
},
"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 {
return invalid_integer_arg(rows);
}
} else {
return Err(USimpleError::new(
1,
get_message_with_args(
"stty-error-invalid-integer-argument",
HashMap::from([("value".to_string(), format!("'{rows}'"))]),
),
));
return missing_arg(arg);
}
} else {
return Err(USimpleError::new(
1,
get_message_with_args(
"stty-error-missing-argument",
HashMap::from([("arg".to_string(), (*arg).to_string())]),
),
));
}
} 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)));
"columns" | "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 {
return invalid_integer_arg(cols);
}
} else {
return Err(USimpleError::new(
1,
get_message_with_args(
"stty-error-invalid-integer-argument",
HashMap::from([("value".to_string(), format!("'{cols}'"))]),
),
));
return missing_arg(arg);
}
}
"drain" => {
set_arg = SetArg::TCSADRAIN;
}
"-drain" => {
set_arg = SetArg::TCSANOW;
}
"size" => {
valid_args.push(ArgOptions::Print(PrintSetting::Size));
}
_ => {
// control char
if let Some(char_index) = cc_to_index(arg) {
if let Some(mapping) = args_iter.next() {
let cc_mapping = string_to_control_char(mapping).map_err(|e| {
let message = match e {
ControlCharMappingError::IntOutOfRange(val) => {
get_message_with_args(
"stty-error-invalid-integer-argument-value-too-large",
HashMap::from([(
"value".to_string(),
format!("'{val}'"),
)]),
)
}
ControlCharMappingError::MultipleChars(val) => {
get_message_with_args(
"stty-error-invalid-integer-argument",
HashMap::from([(
"value".to_string(),
format!("'{val}'"),
)]),
)
}
};
USimpleError::new(1, message)
})?;
valid_args.push(ArgOptions::Mapping((char_index, cc_mapping)));
} else {
return missing_arg(arg);
}
// baud rate
} else if let Some(baud_flag) = string_to_baud(arg) {
valid_args.push(ArgOptions::Flags(baud_flag));
// non control char flag
} else if let Some(flag) = string_to_flag(arg) {
let remove_group = match flag {
AllFlags::Baud(_) => false,
AllFlags::ControlFlags((flag, remove)) => {
check_flag_group(flag, remove)
}
AllFlags::InputFlags((flag, remove)) => check_flag_group(flag, remove),
AllFlags::LocalFlags((flag, remove)) => check_flag_group(flag, remove),
AllFlags::OutputFlags((flag, remove)) => check_flag_group(flag, remove),
};
if remove_group {
return invalid_arg(arg);
}
valid_args.push(flag.into());
// combination setting
} else if let Some(combo) = string_to_combo(arg) {
valid_args.append(&mut combo_to_flags(combo));
} else {
return invalid_arg(arg);
}
} else {
return Err(USimpleError::new(
1,
get_message_with_args(
"stty-error-missing-argument",
HashMap::from([("arg".to_string(), (*arg).to_string())]),
),
));
}
} else if arg == "drain" {
set_arg = SetArg::TCSADRAIN;
} else if arg == "-drain" {
set_arg = SetArg::TCSANOW;
} else if arg == "size" {
valid_args.push(ArgOptions::Print(PrintSetting::Size));
// not a valid option
} else {
return Err(USimpleError::new(
1,
get_message_with_args(
"stty-error-invalid-argument",
HashMap::from([("arg".to_string(), (*arg).to_string())]),
),
));
}
}
@ -490,6 +438,36 @@ fn stty(opts: &Options) -> UResult<()> {
Ok(())
}
fn missing_arg<T>(arg: &str) -> Result<T, Box<dyn UError>> {
Err::<T, Box<dyn UError>>(USimpleError::new(
1,
get_message_with_args(
"stty-error-missing-argument",
HashMap::from([("arg".to_string(), (*arg).to_string())]),
),
))
}
fn invalid_arg<T>(arg: &str) -> Result<T, Box<dyn UError>> {
Err::<T, Box<dyn UError>>(USimpleError::new(
1,
get_message_with_args(
"stty-error-invalid-argument",
HashMap::from([("arg".to_string(), (*arg).to_string())]),
),
))
}
fn invalid_integer_arg<T>(arg: &str) -> Result<T, Box<dyn UError>> {
Err::<T, Box<dyn UError>>(USimpleError::new(
1,
get_message_with_args(
"stty-error-invalid-integer-argument",
HashMap::from([("value".to_string(), format!("'{arg}'"))]),
),
))
}
/// 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> {