1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-29 03:57:44 +00:00

ls: Escape dirname in recursive mode when it has a colon

This commit is contained in:
Dorian Péron 2024-07-10 16:35:55 +02:00
parent 317f6d9a4a
commit 3c5a419537
2 changed files with 48 additions and 15 deletions

View file

@ -55,7 +55,7 @@ use uucore::libc::{dev_t, major, minor};
#[cfg(unix)] #[cfg(unix)]
use uucore::libc::{S_IXGRP, S_IXOTH, S_IXUSR}; use uucore::libc::{S_IXGRP, S_IXOTH, S_IXUSR};
use uucore::line_ending::LineEnding; use uucore::line_ending::LineEnding;
use uucore::quoting_style::{escape_name, QuotingStyle}; use uucore::quoting_style::{escape_dir_name, escape_name, QuotingStyle};
use uucore::{ use uucore::{
display::Quotable, display::Quotable,
error::{set_exit_code, UError, UResult}, error::{set_exit_code, UError, UResult},
@ -2036,14 +2036,27 @@ impl PathData {
} }
} }
/// Show the directory name in the case where several arguments are given to ls
/// or the recursive flag is passed.
///
/// ```no-exec
/// $ ls -R
/// .: <- This is printed by this function
/// dir1 file1 file2
///
/// dir1: <- This as well
/// file11
/// ```
fn show_dir_name(path_data: &PathData, out: &mut BufWriter<Stdout>, config: &Config) { fn show_dir_name(path_data: &PathData, out: &mut BufWriter<Stdout>, config: &Config) {
if config.hyperlink && !config.dired { let escaped_name = escape_dir_name(path_data.p_buf.as_os_str(), &config.quoting_style);
let name = escape_name(path_data.p_buf.as_os_str(), &config.quoting_style);
let hyperlink = create_hyperlink(&name, path_data); let name = if config.hyperlink && !config.dired {
write!(out, "{hyperlink}:").unwrap(); create_hyperlink(&escaped_name, path_data)
} else { } else {
write!(out, "{}:", path_data.p_buf.display()).unwrap(); escaped_name
} };
write!(out, "{name}:").unwrap();
} }
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]
@ -2327,9 +2340,10 @@ fn enter_directory(
for e in entries for e in entries
.iter() .iter()
.skip(if config.files == Files::All { 2 } else { 0 }) .skip(if config.files == Files::All { 2 } else { 0 })
.filter(|p| p.ft.get().is_some()) .filter(|p| {
.filter(|p| p.ft.get().unwrap().is_some()) p.ft.get()
.filter(|p| p.ft.get().unwrap().unwrap().is_dir()) .is_some_and(|o_ft| o_ft.is_some_and(|ft| ft.is_dir()))
})
{ {
match fs::read_dir(&e.p_buf) { match fs::read_dir(&e.p_buf) {
Err(err) => { Err(err) => {

View file

@ -123,7 +123,7 @@ impl EscapedChar {
} }
} }
fn new_c(c: char, quotes: Quotes) -> Self { fn new_c(c: char, quotes: Quotes, dirname: bool) -> Self {
use EscapeState::*; use EscapeState::*;
let init_state = match c { let init_state = match c {
'\x07' => Backslash('a'), '\x07' => Backslash('a'),
@ -142,10 +142,11 @@ impl EscapedChar {
Quotes::Double => Backslash('"'), Quotes::Double => Backslash('"'),
_ => Char('"'), _ => Char('"'),
}, },
' ' => match quotes { ' ' if !dirname => match quotes {
Quotes::None => Backslash(' '), Quotes::None => Backslash(' '),
_ => Char(' '), _ => Char(' '),
}, },
':' if dirname => Backslash(':'),
_ if c.is_ascii_control() => Octal(EscapeOctal::from(c)), _ if c.is_ascii_control() => Octal(EscapeOctal::from(c)),
_ => Char(c), _ => Char(c),
}; };
@ -285,7 +286,10 @@ fn shell_with_escape(name: &str, quotes: Quotes) -> (String, bool) {
} }
/// Escape a name according to the given quoting style. /// Escape a name according to the given quoting style.
pub fn escape_name(name: &OsStr, style: &QuotingStyle) -> String { ///
/// This inner function provides an additional flag `dirname` which
/// is meant for ls' directory name display.
fn escape_name_inner(name: &OsStr, style: &QuotingStyle, dirname: bool) -> String {
match style { match style {
QuotingStyle::Literal { show_control } => { QuotingStyle::Literal { show_control } => {
if *show_control { if *show_control {
@ -301,7 +305,7 @@ pub fn escape_name(name: &OsStr, style: &QuotingStyle) -> String {
let escaped_str: String = name let escaped_str: String = name
.to_string_lossy() .to_string_lossy()
.chars() .chars()
.flat_map(|c| EscapedChar::new_c(c, *quotes)) .flat_map(|c| EscapedChar::new_c(c, *quotes, dirname))
.collect(); .collect();
match quotes { match quotes {
@ -316,7 +320,10 @@ pub fn escape_name(name: &OsStr, style: &QuotingStyle) -> String {
show_control, show_control,
} => { } => {
let name = name.to_string_lossy(); let name = name.to_string_lossy();
let (quotes, must_quote) = if name.contains(&['"', '`', '$', '\\'][..]) {
// Take ':' in account only if we are quoting a dirname
let start_index = if dirname { 0 } else { 1 };
let (quotes, must_quote) = if name.contains(&[':', '"', '`', '$', '\\'][start_index..]) {
(Quotes::Single, true) (Quotes::Single, true)
} else if name.contains('\'') { } else if name.contains('\'') {
(Quotes::Double, true) (Quotes::Double, true)
@ -341,6 +348,18 @@ pub fn escape_name(name: &OsStr, style: &QuotingStyle) -> String {
} }
} }
/// Escape a filename with respect to the given style.
pub fn escape_name(name: &OsStr, style: &QuotingStyle) -> String {
escape_name_inner(name, style, false)
}
/// Escape a directory name with respect to the given style.
/// This is mainly meant to be used for ls' directory name printing and is not
/// likely to be used elsewhere.
pub fn escape_dir_name(dir_name: &OsStr, style: &QuotingStyle) -> String {
escape_name_inner(dir_name, style, true)
}
impl fmt::Display for QuotingStyle { impl fmt::Display for QuotingStyle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self { match *self {