mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
ls: quoting style (#1989)
This commit is contained in:
+108
-2
@@ -13,10 +13,12 @@ extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate uucore;
|
||||
|
||||
mod quoting_style;
|
||||
mod version_cmp;
|
||||
|
||||
use clap::{App, Arg};
|
||||
use number_prefix::NumberPrefix;
|
||||
use quoting_style::{escape_name, QuotingStyle};
|
||||
#[cfg(unix)]
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
@@ -104,6 +106,12 @@ pub mod options {
|
||||
pub static HUMAN_READABLE: &str = "human-readable";
|
||||
pub static SI: &str = "si";
|
||||
}
|
||||
pub mod quoting {
|
||||
pub static ESCAPE: &str = "escape";
|
||||
pub static LITERAL: &str = "literal";
|
||||
pub static C: &str = "quote-name";
|
||||
}
|
||||
pub static QUOTING_STYLE: &str = "quoting-style";
|
||||
|
||||
pub mod indicator_style {
|
||||
pub static NONE: &str = "none";
|
||||
@@ -193,6 +201,7 @@ struct Config {
|
||||
color: bool,
|
||||
long: LongFormat,
|
||||
width: Option<u16>,
|
||||
quoting_style: QuotingStyle,
|
||||
indicator_style: IndicatorStyle,
|
||||
}
|
||||
|
||||
@@ -359,6 +368,51 @@ impl Config {
|
||||
})
|
||||
.or_else(|| termsize::get().map(|s| s.cols));
|
||||
|
||||
let quoting_style = if let Some(style) = options.value_of(options::QUOTING_STYLE) {
|
||||
match style {
|
||||
"literal" => QuotingStyle::Literal,
|
||||
"shell" => QuotingStyle::Shell {
|
||||
escape: false,
|
||||
always_quote: false,
|
||||
},
|
||||
"shell-always" => QuotingStyle::Shell {
|
||||
escape: false,
|
||||
always_quote: true,
|
||||
},
|
||||
"shell-escape" => QuotingStyle::Shell {
|
||||
escape: true,
|
||||
always_quote: false,
|
||||
},
|
||||
"shell-escape-always" => QuotingStyle::Shell {
|
||||
escape: true,
|
||||
always_quote: true,
|
||||
},
|
||||
"c" => QuotingStyle::C {
|
||||
quotes: quoting_style::Quotes::Double,
|
||||
},
|
||||
"escape" => QuotingStyle::C {
|
||||
quotes: quoting_style::Quotes::None,
|
||||
},
|
||||
_ => unreachable!("Should have been caught by Clap"),
|
||||
}
|
||||
} else if options.is_present(options::quoting::LITERAL) {
|
||||
QuotingStyle::Literal
|
||||
} else if options.is_present(options::quoting::ESCAPE) {
|
||||
QuotingStyle::C {
|
||||
quotes: quoting_style::Quotes::None,
|
||||
}
|
||||
} else if options.is_present(options::quoting::C) {
|
||||
QuotingStyle::C {
|
||||
quotes: quoting_style::Quotes::Double,
|
||||
}
|
||||
} else {
|
||||
// TODO: use environment variable if available
|
||||
QuotingStyle::Shell {
|
||||
escape: true,
|
||||
always_quote: false,
|
||||
}
|
||||
};
|
||||
|
||||
let indicator_style = if let Some(field) = options.value_of(options::INDICATOR_STYLE) {
|
||||
match field {
|
||||
"none" => IndicatorStyle::None,
|
||||
@@ -402,6 +456,7 @@ impl Config {
|
||||
inode: options.is_present(options::INODE),
|
||||
long,
|
||||
width,
|
||||
quoting_style,
|
||||
indicator_style,
|
||||
}
|
||||
}
|
||||
@@ -515,6 +570,57 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
.multiple(true)
|
||||
)
|
||||
|
||||
// Quoting style
|
||||
.arg(
|
||||
Arg::with_name(options::QUOTING_STYLE)
|
||||
.long(options::QUOTING_STYLE)
|
||||
.takes_value(true)
|
||||
.help("Set quoting style.")
|
||||
.possible_values(&["literal", "shell", "shell-always", "shell-escape", "shell-escape-always", "c", "escape"])
|
||||
.overrides_with_all(&[
|
||||
options::QUOTING_STYLE,
|
||||
options::quoting::LITERAL,
|
||||
options::quoting::ESCAPE,
|
||||
options::quoting::C,
|
||||
])
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::quoting::LITERAL)
|
||||
.short("N")
|
||||
.long(options::quoting::LITERAL)
|
||||
.help("Use literal quoting style. Equivalent to `--quoting-style=literal`")
|
||||
.overrides_with_all(&[
|
||||
options::QUOTING_STYLE,
|
||||
options::quoting::LITERAL,
|
||||
options::quoting::ESCAPE,
|
||||
options::quoting::C,
|
||||
])
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::quoting::ESCAPE)
|
||||
.short("b")
|
||||
.long(options::quoting::ESCAPE)
|
||||
.help("Use escape quoting style. Equivalent to `--quoting-style=escape`")
|
||||
.overrides_with_all(&[
|
||||
options::QUOTING_STYLE,
|
||||
options::quoting::LITERAL,
|
||||
options::quoting::ESCAPE,
|
||||
options::quoting::C,
|
||||
])
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::quoting::C)
|
||||
.short("Q")
|
||||
.long(options::quoting::C)
|
||||
.help("Use C quoting style. Equivalent to `--quoting-style=c`")
|
||||
.overrides_with_all(&[
|
||||
options::QUOTING_STYLE,
|
||||
options::quoting::LITERAL,
|
||||
options::quoting::ESCAPE,
|
||||
options::quoting::C,
|
||||
])
|
||||
)
|
||||
|
||||
// Time arguments
|
||||
.arg(
|
||||
Arg::with_name(options::TIME)
|
||||
@@ -1206,7 +1312,7 @@ fn display_file_name(
|
||||
metadata: &Metadata,
|
||||
config: &Config,
|
||||
) -> Cell {
|
||||
let mut name = get_file_name(path, strip);
|
||||
let mut name = escape_name(get_file_name(path, strip), &config.quoting_style);
|
||||
let file_type = metadata.file_type();
|
||||
|
||||
match config.indicator_style {
|
||||
@@ -1273,7 +1379,7 @@ fn display_file_name(
|
||||
metadata: &Metadata,
|
||||
config: &Config,
|
||||
) -> Cell {
|
||||
let mut name = get_file_name(path, strip);
|
||||
let mut name = escape_name(get_file_name(path, strip), &config.quoting_style);
|
||||
if config.format != Format::Long && config.inode {
|
||||
name = get_inode(metadata) + " " + &name;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -765,6 +765,8 @@ fn test_ls_ls_color() {
|
||||
.arg("--color=always")
|
||||
.arg("a/nested_file")
|
||||
.succeeds();
|
||||
println!("stderr = {:?}", result.stderr);
|
||||
println!("stdout = {:?}", result.stdout);
|
||||
assert!(result.stdout.contains("a/nested_file\n"));
|
||||
|
||||
// No output
|
||||
@@ -1122,3 +1124,88 @@ fn test_ls_version_sort() {
|
||||
expected.insert(0, ".");
|
||||
assert_eq!(result.stdout.split('\n').collect::<Vec<_>>(), expected,)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ls_quoting_style() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
at.touch("one two");
|
||||
at.touch("one");
|
||||
|
||||
// It seems that windows doesn't allow \n in filenames.
|
||||
#[cfg(unix)]
|
||||
{
|
||||
at.touch("one\ntwo");
|
||||
// Default is shell-escape
|
||||
let result = scene.ucmd().arg("one\ntwo").succeeds();
|
||||
assert_eq!(result.stdout, "'one'$'\\n''two'\n");
|
||||
|
||||
for (arg, correct) in &[
|
||||
("--quoting-style=literal", "one\ntwo"),
|
||||
("-N", "one\ntwo"),
|
||||
("--literal", "one\ntwo"),
|
||||
("--quoting-style=c", "\"one\\ntwo\""),
|
||||
("-Q", "\"one\\ntwo\""),
|
||||
("--quote-name", "\"one\\ntwo\""),
|
||||
("--quoting-style=escape", "one\\ntwo"),
|
||||
("-b", "one\\ntwo"),
|
||||
("--escape", "one\\ntwo"),
|
||||
("--quoting-style=shell-escape", "'one'$'\\n''two'"),
|
||||
("--quoting-style=shell-escape-always", "'one'$'\\n''two'"),
|
||||
("--quoting-style=shell", "one?two"),
|
||||
("--quoting-style=shell-always", "'one?two'"),
|
||||
] {
|
||||
let result = scene.ucmd().arg(arg).arg("one\ntwo").run();
|
||||
println!("stderr = {:?}", result.stderr);
|
||||
println!("stdout = {:?}", result.stdout);
|
||||
assert_eq!(result.stdout, format!("{}\n", correct));
|
||||
}
|
||||
}
|
||||
|
||||
let result = scene.ucmd().arg("one two").succeeds();
|
||||
assert_eq!(result.stdout, "'one two'\n");
|
||||
|
||||
for (arg, correct) in &[
|
||||
("--quoting-style=literal", "one two"),
|
||||
("-N", "one two"),
|
||||
("--literal", "one two"),
|
||||
("--quoting-style=c", "\"one two\""),
|
||||
("-Q", "\"one two\""),
|
||||
("--quote-name", "\"one two\""),
|
||||
("--quoting-style=escape", "one\\ two"),
|
||||
("-b", "one\\ two"),
|
||||
("--escape", "one\\ two"),
|
||||
("--quoting-style=shell-escape", "'one two'"),
|
||||
("--quoting-style=shell-escape-always", "'one two'"),
|
||||
("--quoting-style=shell", "'one two'"),
|
||||
("--quoting-style=shell-always", "'one two'"),
|
||||
] {
|
||||
let result = scene.ucmd().arg(arg).arg("one two").run();
|
||||
println!("stderr = {:?}", result.stderr);
|
||||
println!("stdout = {:?}", result.stdout);
|
||||
assert_eq!(result.stdout, format!("{}\n", correct));
|
||||
}
|
||||
|
||||
let result = scene.ucmd().arg("one").succeeds();
|
||||
assert_eq!(result.stdout, "one\n");
|
||||
|
||||
for (arg, correct) in &[
|
||||
("--quoting-style=literal", "one"),
|
||||
("-N", "one"),
|
||||
("--quoting-style=c", "\"one\""),
|
||||
("-Q", "\"one\""),
|
||||
("--quote-name", "\"one\""),
|
||||
("--quoting-style=escape", "one"),
|
||||
("-b", "one"),
|
||||
("--quoting-style=shell-escape", "one"),
|
||||
("--quoting-style=shell-escape-always", "'one'"),
|
||||
("--quoting-style=shell", "one"),
|
||||
("--quoting-style=shell-always", "'one'"),
|
||||
] {
|
||||
let result = scene.ucmd().arg(arg).arg("one").run();
|
||||
println!("stderr = {:?}", result.stderr);
|
||||
println!("stdout = {:?}", result.stdout);
|
||||
assert_eq!(result.stdout, format!("{}\n", correct));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user