mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 11:07:44 +00:00
wc: fix escaping
GNU wc only escapes file names with newlines in them.
This commit is contained in:
parent
d3db8dc29f
commit
ff8a31e835
4 changed files with 51 additions and 14 deletions
|
@ -34,6 +34,8 @@ windows = ["feat_os_windows"]
|
||||||
nightly = []
|
nightly = []
|
||||||
test_unimplemented = []
|
test_unimplemented = []
|
||||||
expensive_tests = []
|
expensive_tests = []
|
||||||
|
# "test_risky_names" == enable tests that create problematic file names (would make a network share inaccessible to Windows, breaks SVN on Mac OS, etc.)
|
||||||
|
test_risky_names = []
|
||||||
# * only build `uudoc` when `--feature uudoc` is activated
|
# * only build `uudoc` when `--feature uudoc` is activated
|
||||||
uudoc = ["zip", "dep:uuhelp_parser"]
|
uudoc = ["zip", "dep:uuhelp_parser"]
|
||||||
## features
|
## features
|
||||||
|
|
4
build.rs
4
build.rs
|
@ -33,7 +33,9 @@ pub fn main() {
|
||||||
#[allow(clippy::match_same_arms)]
|
#[allow(clippy::match_same_arms)]
|
||||||
match krate.as_ref() {
|
match krate.as_ref() {
|
||||||
"default" | "macos" | "unix" | "windows" | "selinux" | "zip" => continue, // common/standard feature names
|
"default" | "macos" | "unix" | "windows" | "selinux" | "zip" => continue, // common/standard feature names
|
||||||
"nightly" | "test_unimplemented" | "expensive_tests" => continue, // crate-local custom features
|
"nightly" | "test_unimplemented" | "expensive_tests" | "test_risky_names" => {
|
||||||
|
continue
|
||||||
|
} // crate-local custom features
|
||||||
"uudoc" => continue, // is not a utility
|
"uudoc" => continue, // is not a utility
|
||||||
"test" => continue, // over-ridden with 'uu_test' to avoid collision with rust core crate 'test'
|
"test" => continue, // over-ridden with 'uu_test' to avoid collision with rust core crate 'test'
|
||||||
s if s.starts_with(FEATURE_PREFIX) => continue, // crate feature sets
|
s if s.starts_with(FEATURE_PREFIX) => continue, // crate feature sets
|
||||||
|
|
|
@ -255,13 +255,17 @@ impl<'a> Input<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts input to title that appears in stats.
|
/// Converts input to title that appears in stats.
|
||||||
fn to_title(&self) -> Option<Cow<str>> {
|
fn to_title(&self) -> Option<Cow<OsStr>> {
|
||||||
match self {
|
match self {
|
||||||
Self::Path(path) => Some(match path.to_str() {
|
Self::Path(path) => {
|
||||||
Some(s) if !s.contains('\n') => Cow::Borrowed(s),
|
let path = path.as_os_str();
|
||||||
_ => Cow::Owned(escape_name_wrapper(path.as_os_str())),
|
if path.to_string_lossy().contains('\n') {
|
||||||
}),
|
Some(Cow::Owned(quoting_style::escape_name(path, QS_ESCAPE)))
|
||||||
Self::Stdin(StdinKind::Explicit) => Some(Cow::Borrowed(STDIN_REPR)),
|
} else {
|
||||||
|
Some(Cow::Borrowed(path))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Stdin(StdinKind::Explicit) => Some(Cow::Borrowed(OsStr::new(STDIN_REPR))),
|
||||||
Self::Stdin(StdinKind::Implicit) => None,
|
Self::Stdin(StdinKind::Implicit) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -852,14 +856,17 @@ fn wc(inputs: &Inputs, settings: &Settings) -> UResult<()> {
|
||||||
let maybe_title = input.to_title();
|
let maybe_title = input.to_title();
|
||||||
let maybe_title_str = maybe_title.as_deref();
|
let maybe_title_str = maybe_title.as_deref();
|
||||||
if let Err(err) = print_stats(settings, &word_count, maybe_title_str, number_width) {
|
if let Err(err) = print_stats(settings, &word_count, maybe_title_str, number_width) {
|
||||||
let title = maybe_title_str.unwrap_or("<stdin>");
|
let title = maybe_title_str.unwrap_or(OsStr::new("<stdin>"));
|
||||||
show!(err.map_err_context(|| format!("failed to print result for {title}")));
|
show!(err.map_err_context(|| format!(
|
||||||
|
"failed to print result for {}",
|
||||||
|
title.to_string_lossy()
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if settings.total_when.is_total_row_visible(num_inputs) {
|
if settings.total_when.is_total_row_visible(num_inputs) {
|
||||||
let title = are_stats_visible.then_some("total");
|
let title = are_stats_visible.then_some(OsStr::new("total"));
|
||||||
if let Err(err) = print_stats(settings, &total_word_count, title, number_width) {
|
if let Err(err) = print_stats(settings, &total_word_count, title, number_width) {
|
||||||
show!(err.map_err_context(|| "failed to print total".into()));
|
show!(err.map_err_context(|| "failed to print total".into()));
|
||||||
}
|
}
|
||||||
|
@ -873,7 +880,7 @@ fn wc(inputs: &Inputs, settings: &Settings) -> UResult<()> {
|
||||||
fn print_stats(
|
fn print_stats(
|
||||||
settings: &Settings,
|
settings: &Settings,
|
||||||
result: &WordCount,
|
result: &WordCount,
|
||||||
title: Option<&str>,
|
title: Option<&OsStr>,
|
||||||
number_width: usize,
|
number_width: usize,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
let mut stdout = io::stdout().lock();
|
let mut stdout = io::stdout().lock();
|
||||||
|
@ -893,8 +900,8 @@ fn print_stats(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(title) = title {
|
if let Some(title) = title {
|
||||||
writeln!(stdout, "{space}{title}")
|
write!(stdout, "{space}")?;
|
||||||
} else {
|
stdout.write_all(&uucore::os_str_as_bytes_lossy(title))?;
|
||||||
writeln!(stdout)
|
|
||||||
}
|
}
|
||||||
|
writeln!(stdout)
|
||||||
}
|
}
|
||||||
|
|
|
@ -283,6 +283,32 @@ fn test_gnu_compatible_quotation() {
|
||||||
.stdout_is("0 0 0 'some-dir1/12'$'\\n''34.txt'\n");
|
.stdout_is("0 0 0 'some-dir1/12'$'\\n''34.txt'\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "test_risky_names")]
|
||||||
|
#[test]
|
||||||
|
fn test_non_unicode_names() {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
let target1 = uucore::os_str_from_bytes(b"some-dir1/1\xC0\n.txt")
|
||||||
|
.expect("Only unix platforms can test non-unicode names");
|
||||||
|
let target2 = uucore::os_str_from_bytes(b"some-dir1/2\xC0\t.txt")
|
||||||
|
.expect("Only unix platforms can test non-unicode names");
|
||||||
|
let at = &scene.fixtures;
|
||||||
|
at.mkdir("some-dir1");
|
||||||
|
at.touch(&target1);
|
||||||
|
at.touch(&target2);
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.args(&[target1, target2])
|
||||||
|
.run()
|
||||||
|
.stdout_is_bytes(
|
||||||
|
[
|
||||||
|
b"0 0 0 'some-dir1/1'$'\\300\\n''.txt'\n".to_vec(),
|
||||||
|
b"0 0 0 some-dir1/2\xC0\t.txt\n".to_vec(),
|
||||||
|
b"0 0 0 total\n".to_vec(),
|
||||||
|
]
|
||||||
|
.concat(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_multiple_default() {
|
fn test_multiple_default() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue