This commit is contained in:
Scott Baker 2024-04-10 18:04:48 +02:00 committed by GitHub
commit 7eba1242f4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 92 additions and 3 deletions

View File

@ -630,6 +630,14 @@ pub struct Opts {
#[arg(long, aliases(&["mount", "xdev"]), hide_short_help = true, long_help)]
pub one_file_system: bool,
/// By default we output matched files/dirs raw. When the user specifies
/// --quote we output the files wrapped in quotes per the rules laid out
/// in coreutils: https://www.gnu.org/software/coreutils/quotes.html
/// This should mimic the `ls -lsa` output style
#[cfg(any(unix, windows))]
#[arg(long, aliases(&["quote"]), hide_short_help = true, long_help)]
pub use_quoting: bool,
#[cfg(feature = "completions")]
#[arg(long, hide = true, exclusive = true)]
gen_completions: Option<Option<Shell>>,

View File

@ -122,6 +122,9 @@ pub struct Config {
/// Whether or not to strip the './' prefix for search results
pub strip_cwd_prefix: bool,
/// Whether to use quoting on the output file names
pub use_quoting: bool,
}
impl Config {

View File

View File

View File

@ -251,6 +251,7 @@ fn construct_config(mut opts: Opts, pattern_regexps: &[String]) -> Result<Config
one_file_system: opts.one_file_system,
null_separator: opts.null_separator,
quiet: opts.quiet,
use_quoting: opts.use_quoting,
max_depth: opts.max_depth(),
min_depth: opts.min_depth(),
prune: opts.prune,

View File

@ -54,6 +54,43 @@ fn print_trailing_slash<W: Write>(
Ok(())
}
// Trying to copy: https://www.gnu.org/software/coreutils/quotes.html
fn path_needs_quoting(path: &str) -> i8 {
// If it contains any special chars we return single quote
if path.contains(" ") || path.contains("$") || path.contains("\"") {
return 1;
// If it ONLY contains a ' we return double quote
} else if path.contains("'") {
return 2;
}
return 0;
}
// Quote a path with coreutils style quoting to make copy/paste
// more friendly for shells
fn quote_path(path_str: &str) -> String {
let quote_type = path_needs_quoting(path_str);
let mut tmp_str:String = path_str.into();
// Quote with single quotes
if quote_type == 1 {
// Escape single quotes in path
tmp_str = str::replace(&tmp_str, "'", "'\\''");
format!("'{}'", tmp_str)
// Quote with double quotes
} else if quote_type == 2 {
// Escape double quotes in path
tmp_str = str::replace(&tmp_str, "\"", "\\\"");
format!("\"{}\"", tmp_str)
// No quoting required
} else {
path_str.to_string()
}
}
// TODO: this function is performance critical and can probably be optimized
fn print_entry_colorized<W: Write>(
stdout: &mut W,
@ -62,9 +99,22 @@ fn print_entry_colorized<W: Write>(
ls_colors: &LsColors,
) -> io::Result<()> {
// Split the path between the parent and the last component
let mut offset = 0;
let path = entry.stripped_path(config);
let path_str = path.to_string_lossy();
let mut offset = 0;
let path = entry.stripped_path(config);
let mut path_str = path.to_string_lossy();
let mut needs_quoting = false;
// Wrap the path in quotes
if config.use_quoting {
let tmp_str = quote_path(&path_str);
// If the quoted string is new, then we flag that to tweak the offset
// so the colors line up
if tmp_str != path_str {
path_str = tmp_str.into();
needs_quoting = true;
}
}
if let Some(parent) = path.parent() {
offset = parent.to_string_lossy().len();
@ -78,6 +128,11 @@ fn print_entry_colorized<W: Write>(
}
if offset > 0 {
if needs_quoting {
offset += 2;
}
let mut parent_str = Cow::from(&path_str[..offset]);
if let Some(ref separator) = config.path_separator {
*parent_str.to_mut() = replace_path_separator(&parent_str, separator);

View File

@ -78,6 +78,14 @@ fn test_simple() {
);
}
static AND_QUOTE_FILES: &[&str] = &[
"one'two.quo",
"one two.quo",
"one\"two.quo",
"one$two.quo",
"one'two$.quo",
];
static AND_EXTRA_FILES: &[&str] = &[
"a.foo",
"one/b.foo",
@ -2534,6 +2542,20 @@ fn test_strip_cwd_prefix() {
);
}
#[test]
fn test_quoting() {
let te = TestEnv::new(DEFAULT_DIRS, AND_QUOTE_FILES);
te.assert_output(
&["--quote", ".quo"],
"'one two.quo'
\"one'two.quo\"
\"one'two\\$.quo\"
'one\"two.quo'
'one$two.quo'",
);
}
/// When fd is ran from a non-existent working directory, but an existent
/// directory is passed in the arguments, it should still run fine
#[test]