Skip to content

Commit 24fa079

Browse files
committed
refactor(stty): optimize termios fetching and fix save format parsing
Reuse base termios struct from target device to avoid redundant tcgetattr calls, improve performance, and ensure correctness in parse_save_format by cloning the base instead of hardcoding stdout. Also add GNU stty-compatible error handling for invalid save format with colons.
1 parent 4f4ec2c commit 24fa079

File tree

1 file changed

+18
-18
lines changed

1 file changed

+18
-18
lines changed

src/uu/stty/src/stty.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,9 @@ fn stty(opts: &Options) -> UResult<()> {
264264
));
265265
}
266266

267+
// Fetch termios for the target device once and reuse it downstream
268+
let base_termios = tcgetattr(opts.file.as_fd())?;
269+
267270
let mut set_arg = SetArg::TCSADRAIN;
268271
let mut valid_args: Vec<ArgOptions> = Vec::new();
269272

@@ -274,11 +277,16 @@ fn stty(opts: &Options) -> UResult<()> {
274277

275278
if let Some(args) = &opts.settings {
276279
if let Some((first, rest)) = args.split_first() {
277-
if let Ok(termios) = parse_save_format(first) {
278-
restored_from_save = Some(termios);
279-
settings_iter = Box::new(rest.iter().copied());
280-
} else {
281-
settings_iter = Box::new(args.iter().map(|s| &**s));
280+
match parse_save_format(first, &base_termios) {
281+
Ok(termios) => {
282+
restored_from_save = Some(termios);
283+
settings_iter = Box::new(rest.iter().copied());
284+
}
285+
// GNU stty は先頭が save 形式っぽいのにパースできなければ即エラーにする
286+
Err(e) if first.contains(':') => return Err(e),
287+
Err(_) => {
288+
settings_iter = Box::new(args.iter().map(|s| &**s));
289+
}
282290
}
283291
}
284292
}
@@ -426,11 +434,7 @@ fn stty(opts: &Options) -> UResult<()> {
426434
}
427435

428436
// TODO: Figure out the right error message for when tcgetattr fails
429-
let mut termios = if let Some(restored) = restored_from_save {
430-
restored
431-
} else {
432-
tcgetattr(opts.file.as_fd())?
433-
};
437+
let mut termios = restored_from_save.unwrap_or(base_termios);
434438

435439
// iterate over valid_args, match on the arg type, do the matching apply function
436440
for arg in &valid_args {
@@ -447,9 +451,7 @@ fn stty(opts: &Options) -> UResult<()> {
447451
}
448452
tcsetattr(opts.file.as_fd(), set_arg, &termios)?;
449453
} else {
450-
// TODO: Figure out the right error message for when tcgetattr fails
451-
let termios = tcgetattr(opts.file.as_fd())?;
452-
print_settings(&termios, opts)?;
454+
print_settings(&base_termios, opts)?;
453455
}
454456
Ok(())
455457
}
@@ -719,7 +721,7 @@ fn print_in_save_format(termios: &Termios) {
719721

720722
/// GNU stty -g compatibility: restore Termios from the colon-separated hexadecimal representation
721723
/// produced by print_in_save_format.
722-
fn parse_save_format(s: &str) -> Result<Termios, Box<dyn UError>> {
724+
fn parse_save_format(s: &str, base: &Termios) -> Result<Termios, Box<dyn UError>> {
723725
// Expect four flag values + a variable-length sequence of cc_t values (at least one).
724726
let parts: Vec<&str> = s.split(':').collect();
725727
if parts.len() < 5 {
@@ -747,10 +749,8 @@ fn parse_save_format(s: &str) -> Result<Termios, Box<dyn UError>> {
747749
// Remaining segments are control_chars.
748750
let cc_hex = &parts[4..];
749751

750-
// Obtain the original termios and overwrite specific fields on top of it to preserve
751-
// platform-dependent fields.
752-
let mut termios = tcgetattr(std::io::stdout().as_fd())
753-
.map_err(|_| USimpleError::new(1, translate!("stty-error-io")))?;
752+
// base で渡されたデバイスの termios をコピーし、プラットフォーム依存フィールドを保持する
753+
let mut termios = base.clone();
754754

755755
termios.input_flags = InputFlags::from_bits_truncate(iflags_bits);
756756
termios.output_flags = OutputFlags::from_bits_truncate(oflags_bits);

0 commit comments

Comments
 (0)