From 6c9e743d43ff2daff39aeab0796ae713bb544263 Mon Sep 17 00:00:00 2001 From: sharkdp Date: Sat, 4 Apr 2020 12:51:15 +0200 Subject: [PATCH] Print path as raw bytes, allowing invalid UTF-8 filenames to be passed to other processes --- src/filesystem.rs | 4 ---- src/main.rs | 4 +++- src/options.rs | 3 +++ src/output.rs | 36 ++++++++++++++++++++++++++++++++++-- 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/filesystem.rs b/src/filesystem.rs index 59d9acd..a43527f 100644 --- a/src/filesystem.rs +++ b/src/filesystem.rs @@ -88,10 +88,6 @@ pub fn strip_current_dir(path: &Path) -> &Path { path.strip_prefix(".").unwrap_or(path) } -pub fn replace_path_separator<'a>(path: &str, new_path_separator: &str) -> String { - path.replace(std::path::MAIN_SEPARATOR, &new_path_separator) -} - #[cfg(test)] mod tests { use super::strip_current_dir; diff --git a/src/main.rs b/src/main.rs index 7373002..69a61d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -137,10 +137,11 @@ fn run() -> Result { let case_sensitive = !matches.is_present("ignore-case") && (matches.is_present("case-sensitive") || pattern_has_uppercase_char(&pattern_regex)); + let interactive_terminal = atty::is(Stream::Stdout); let colored_output = match matches.value_of("color") { Some("always") => true, Some("never") => false, - _ => env::var_os("NO_COLOR").is_none() && atty::is(Stream::Stdout), + _ => env::var_os("NO_COLOR").is_none() && interactive_terminal, }; let path_separator = matches.value_of("path-separator").map(|str| str.to_owned()); @@ -240,6 +241,7 @@ fn run() -> Result { .and_then(|n| u64::from_str_radix(n, 10).ok()) .map(time::Duration::from_millis), ls_colors, + interactive_terminal, file_types: matches.values_of("file-type").map(|values| { let mut file_types = FileTypes::default(); for value in values { diff --git a/src/options.rs b/src/options.rs index d0620c9..e7fb445 100644 --- a/src/options.rs +++ b/src/options.rs @@ -52,6 +52,9 @@ pub struct Options { /// how to style different filetypes. pub ls_colors: Option, + /// Whether or not we are writing to an interactive terminal + pub interactive_terminal: bool, + /// The type of file to search for. If set to `None`, all file types are displayed. If /// set to `Some(..)`, only the types that are specified are shown. pub file_types: Option, diff --git a/src/output.rs b/src/output.rs index fcc9ed7..175b1dd 100644 --- a/src/output.rs +++ b/src/output.rs @@ -8,9 +8,13 @@ use ansi_term; use lscolors::{LsColors, Style}; use crate::exit_codes::ExitCode; -use crate::filesystem::{replace_path_separator, strip_current_dir}; +use crate::filesystem::strip_current_dir; use crate::options::Options; +pub fn replace_path_separator<'a>(path: &str, new_path_separator: &str) -> String { + path.replace(std::path::MAIN_SEPARATOR, &new_path_separator) +} + // TODO: this function is performance critical and can probably be optimized pub fn print_entry( stdout: &mut StdoutLock, @@ -73,7 +77,7 @@ fn print_entry_colorized( } // TODO: this function is performance critical and can probably be optimized -fn print_entry_uncolorized( +fn print_entry_uncolorized_base( stdout: &mut StdoutLock, path: &Path, config: &Options, @@ -86,3 +90,31 @@ fn print_entry_uncolorized( } write!(stdout, "{}{}", path_string, separator) } + +#[cfg(not(unix))] +fn print_entry_uncolorized( + stdout: &mut StdoutLock, + path: &Path, + config: &Options, +) -> io::Result<()> { + print_entry_uncolorized_base(stdout, path, config) +} + +#[cfg(unix)] +fn print_entry_uncolorized( + stdout: &mut StdoutLock, + path: &Path, + config: &Options, +) -> io::Result<()> { + use std::os::unix::ffi::OsStrExt; + + if config.interactive_terminal || config.path_separator.is_some() { + // Fall back to the base implementation + print_entry_uncolorized_base(stdout, path, config) + } else { + // Print path as raw bytes, allowing invalid UTF-8 filenames to be passed to other processes + let separator = if config.null_separator { b"\0" } else { b"\n" }; + stdout.write_all(path.as_os_str().as_bytes())?; + stdout.write_all(separator) + } +}