ls: quoting style (#1989)

This commit is contained in:
Terts Diepraam
2021-04-01 22:50:13 +02:00
committed by GitHub
parent cc9c846032
commit 2941dfd698
3 changed files with 737 additions and 2 deletions
+108 -2
View File
@@ -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
+87
View File
@@ -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));
}
}