-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
stty: report write errors instead of panicking (#10552) #13252
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -37,12 +37,13 @@ use nix::sys::termios::{ | |||||
| use nix::{ioctl_read_bad, ioctl_write_ptr_bad}; | ||||||
| use std::cmp::Ordering; | ||||||
| use std::fs::File; | ||||||
| use std::io::{self, Stdin, stdin, stdout}; | ||||||
| use std::io::{self, Stdin, Write, stdin, stdout}; | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Traits imported that aren’t used directly can be imported anonymously with |
||||||
| 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::{FromIo, UError, UResult, USimpleError, UUsageError}; | ||||||
| use thiserror::Error; | ||||||
| use uucore::error::{FromIo, UError, UResult, USimpleError, UUsageError, strip_errno}; | ||||||
| use uucore::format_usage; | ||||||
| use uucore::parser::num_parser::ExtendedParser; | ||||||
| use uucore::translate; | ||||||
|
|
@@ -552,17 +553,27 @@ 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<()> { | ||||||
| fn print_special_setting(setting: &PrintSetting, fd: i32) -> UResult<()> { | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
change return type to be more specific where possible |
||||||
| match setting { | ||||||
| PrintSetting::Size => { | ||||||
| let mut size = TermSize::default(); | ||||||
| unsafe { tiocgwinsz(fd, &raw mut size)? }; | ||||||
| println!("{} {}", size.rows, size.columns); | ||||||
| writeln!(stdout(), "{} {}", size.rows, size.columns).map_err(SttyError::Write)?; | ||||||
| } | ||||||
| } | ||||||
| Ok(()) | ||||||
| } | ||||||
|
|
||||||
| #[derive(Debug, Error)] | ||||||
| enum SttyError { | ||||||
| /// A write to stdout failed. Report instead of panicking the way | ||||||
| /// 'print!' family of macros does. | ||||||
| #[error("{}: {}", translate!("stty-error-write-error"), strip_errno(.0))] | ||||||
| Write(io::Error), | ||||||
| } | ||||||
|
|
||||||
| impl UError for SttyError {} | ||||||
|
|
||||||
| /// Handles line wrapping for stty output to fit within terminal width | ||||||
| struct WrappedPrinter { | ||||||
| width: usize, | ||||||
|
|
@@ -593,29 +604,31 @@ impl WrappedPrinter { | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| fn print(&mut self, token: &str) { | ||||||
| fn print(&mut self, token: &str) -> UResult<()> { | ||||||
| let token_len = self.prefix().chars().count() + token.chars().count(); | ||||||
| if self.current > 0 && self.current + token_len > self.width { | ||||||
| println!(); | ||||||
| writeln!(stdout()).map_err(SttyError::Write)?; | ||||||
| self.current = 0; | ||||||
| self.first_in_line = true; | ||||||
| } | ||||||
|
|
||||||
| print!("{}{token}", self.prefix()); | ||||||
| write!(stdout(), "{}{token}", self.prefix()).map_err(SttyError::Write)?; | ||||||
| self.current += token_len; | ||||||
| self.first_in_line = false; | ||||||
| Ok(()) | ||||||
| } | ||||||
|
|
||||||
| fn prefix(&self) -> &str { | ||||||
| if self.first_in_line { "" } else { " " } | ||||||
| } | ||||||
|
|
||||||
| fn flush(&mut self) { | ||||||
| fn flush(&mut self) -> UResult<()> { | ||||||
| if self.current > 0 { | ||||||
| println!(); | ||||||
| writeln!(stdout()).map_err(SttyError::Write)?; | ||||||
| self.current = 0; | ||||||
| self.first_in_line = false; | ||||||
| } | ||||||
| Ok(()) | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -628,7 +641,7 @@ fn print_terminal_size( | |||||
| opts: &Options, | ||||||
| window_size: Option<&TermSize>, | ||||||
| term_size: Option<&TermSize>, | ||||||
| ) -> nix::Result<()> { | ||||||
| ) -> UResult<()> { | ||||||
| // GNU linked against glibc 2.42 provides us baudrate 51 which panics cfgetospeed | ||||||
| #[cfg(not(target_os = "linux"))] | ||||||
| let speed = nix::sys::termios::cfgetospeed(termios); | ||||||
|
|
@@ -658,7 +671,7 @@ fn print_terminal_size( | |||||
| not(all(target_arch = "powerpc64", target_endian = "big")) | ||||||
| ) | ||||||
| ))] | ||||||
| printer.print(&translate!("stty-output-speed", "speed" => speed)); | ||||||
| printer.print(&translate!("stty-output-speed", "speed" => speed))?; | ||||||
|
|
||||||
| // Big-endian Linux PowerPC uses BaudRate enum, need to convert to display format | ||||||
| #[cfg(target_os = "linux")] | ||||||
|
|
@@ -673,15 +686,15 @@ fn print_terminal_size( | |||||
| .find(|(_, rate)| *rate == speed) | ||||||
| .map(|(text, _)| *text) | ||||||
| .unwrap_or("unknown"); | ||||||
| printer.print(&translate!("stty-output-speed", "speed" => speed_str)); | ||||||
| printer.print(&translate!("stty-output-speed", "speed" => speed_str))?; | ||||||
| } | ||||||
|
|
||||||
| // Other platforms need to use the baud rate enum, so printing the right value | ||||||
| // becomes slightly more complicated. | ||||||
| #[cfg(not(any(target_os = "linux", bsd)))] | ||||||
| for (text, baud_rate) in BAUD_RATES { | ||||||
| if *baud_rate == speed { | ||||||
| printer.print(&translate!("stty-output-speed", "speed" => (*text))); | ||||||
| printer.print(&translate!("stty-output-speed", "speed" => (*text)))?; | ||||||
| break; | ||||||
| } | ||||||
| } | ||||||
|
|
@@ -690,7 +703,7 @@ fn print_terminal_size( | |||||
| let term_size = term_size.as_ref().expect("terminal size should be set"); | ||||||
| printer.print( | ||||||
| &translate!("stty-output-rows-columns", "rows" => term_size.rows, "columns" => term_size.columns), | ||||||
| ); | ||||||
| )?; | ||||||
| } | ||||||
|
|
||||||
| #[cfg(any(target_os = "linux", target_os = "redox"))] | ||||||
|
|
@@ -699,9 +712,9 @@ fn print_terminal_size( | |||||
| // so we get the underlying libc::termios struct to get that information. | ||||||
| let libc_termios: nix::libc::termios = termios.clone().into(); | ||||||
| let line = libc_termios.c_line; | ||||||
| printer.print(&translate!("stty-output-line", "line" => line)); | ||||||
| printer.print(&translate!("stty-output-line", "line" => line))?; | ||||||
| } | ||||||
| printer.flush(); | ||||||
| printer.flush()?; | ||||||
| Ok(()) | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -859,7 +872,7 @@ fn print_control_chars( | |||||
| termios: &Termios, | ||||||
| opts: &Options, | ||||||
| term_size: Option<&TermSize>, | ||||||
| ) -> nix::Result<()> { | ||||||
| ) -> UResult<()> { | ||||||
| if !opts.all { | ||||||
| // Print only control chars that differ from sane defaults | ||||||
| let mut printer = WrappedPrinter::new(term_size); | ||||||
|
|
@@ -871,10 +884,10 @@ fn print_control_chars( | |||||
| printer.print(&format!( | ||||||
| "{text} = {};", | ||||||
| control_char_to_string(current_val)? | ||||||
| )); | ||||||
| ))?; | ||||||
| } | ||||||
| } | ||||||
| printer.flush(); | ||||||
| printer.flush()?; | ||||||
| return Ok(()); | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -883,28 +896,31 @@ fn print_control_chars( | |||||
| printer.print(&format!( | ||||||
| "{text} = {};", | ||||||
| control_char_to_string(termios.control_chars[*cc_index as usize])? | ||||||
| )); | ||||||
| ))?; | ||||||
| } | ||||||
| printer.print(&translate!("stty-output-min-time", | ||||||
| "min" => termios.control_chars[S::VMIN as usize], | ||||||
| "time" => termios.control_chars[S::VTIME as usize] | ||||||
| )); | ||||||
| printer.flush(); | ||||||
| ))?; | ||||||
| printer.flush()?; | ||||||
| Ok(()) | ||||||
| } | ||||||
|
|
||||||
| fn print_in_save_format(termios: &Termios) { | ||||||
| print!( | ||||||
| fn print_in_save_format(termios: &Termios) -> UResult<()> { | ||||||
| write!( | ||||||
| stdout(), | ||||||
| "{:x}:{:x}:{:x}:{:x}", | ||||||
| termios.input_flags.bits(), | ||||||
| termios.output_flags.bits(), | ||||||
| termios.control_flags.bits(), | ||||||
| termios.local_flags.bits() | ||||||
| ); | ||||||
| ) | ||||||
| .map_err(SttyError::Write)?; | ||||||
| for cc in termios.control_chars { | ||||||
| print!(":{cc:x}"); | ||||||
| write!(stdout(), ":{cc:x}").map_err(SttyError::Write)?; | ||||||
| } | ||||||
| println!(); | ||||||
| writeln!(stdout()).map_err(SttyError::Write)?; | ||||||
| Ok(()) | ||||||
| } | ||||||
|
|
||||||
| /// Gets terminal size using the tiocgwinsz ioctl system call. | ||||||
|
|
@@ -914,9 +930,9 @@ fn get_terminal_size(fd: RawFd) -> nix::Result<TermSize> { | |||||
| unsafe { tiocgwinsz(fd, &raw mut term_size) }.map(|_| term_size) | ||||||
| } | ||||||
|
|
||||||
| fn print_settings(termios: &Termios, opts: &Options) -> nix::Result<()> { | ||||||
| fn print_settings(termios: &Termios, opts: &Options) -> UResult<()> { | ||||||
| if opts.save { | ||||||
| print_in_save_format(termios); | ||||||
| print_in_save_format(termios)?; | ||||||
| } else { | ||||||
| let device_fd = opts.file.as_raw_fd(); | ||||||
| let term_size = if opts.all { | ||||||
|
|
@@ -934,10 +950,10 @@ fn print_settings(termios: &Termios, opts: &Options) -> nix::Result<()> { | |||||
|
|
||||||
| print_terminal_size(termios, opts, window_size.as_ref(), term_size.as_ref())?; | ||||||
| print_control_chars(termios, opts, window_size.as_ref())?; | ||||||
| print_flags(termios, opts, CONTROL_FLAGS, window_size.as_ref()); | ||||||
| print_flags(termios, opts, INPUT_FLAGS, window_size.as_ref()); | ||||||
| print_flags(termios, opts, OUTPUT_FLAGS, window_size.as_ref()); | ||||||
| print_flags(termios, opts, LOCAL_FLAGS, window_size.as_ref()); | ||||||
| print_flags(termios, opts, CONTROL_FLAGS, window_size.as_ref())?; | ||||||
| print_flags(termios, opts, INPUT_FLAGS, window_size.as_ref())?; | ||||||
| print_flags(termios, opts, OUTPUT_FLAGS, window_size.as_ref())?; | ||||||
| print_flags(termios, opts, LOCAL_FLAGS, window_size.as_ref())?; | ||||||
| } | ||||||
| Ok(()) | ||||||
| } | ||||||
|
|
@@ -947,7 +963,7 @@ fn print_flags<T: TermiosFlag>( | |||||
| opts: &Options, | ||||||
| flags: &[Flag<T>], | ||||||
| term_size: Option<&TermSize>, | ||||||
| ) { | ||||||
| ) -> UResult<()> { | ||||||
| let mut printer = WrappedPrinter::new(term_size); | ||||||
| for &Flag { | ||||||
| name, | ||||||
|
|
@@ -963,17 +979,17 @@ fn print_flags<T: TermiosFlag>( | |||||
| let val = flag.is_in(termios, group); | ||||||
| if group.is_some() { | ||||||
| if val && (!sane || opts.all) { | ||||||
| printer.print(name); | ||||||
| printer.print(name)?; | ||||||
| } | ||||||
| } else if opts.all || val != sane { | ||||||
| if !val { | ||||||
| printer.print(&format!("-{name}")); | ||||||
| printer.print(&format!("-{name}"))?; | ||||||
| continue; | ||||||
| } | ||||||
| printer.print(name); | ||||||
| printer.print(name)?; | ||||||
| } | ||||||
| } | ||||||
| printer.flush(); | ||||||
| printer.flush() | ||||||
| } | ||||||
|
|
||||||
| /// Apply a single setting | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we don't need this string, we can use
common-write-errorinstead