From 8a03ac6caa4095a14ef1f3d84db67e9dd4bf8b6e Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Thu, 10 Jun 2021 12:03:25 +0900 Subject: [PATCH 01/37] Prevent double scanning from dircolors --- src/uu/dircolors/src/dircolors.rs | 35 +++++++++++++++++-------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/uu/dircolors/src/dircolors.rs b/src/uu/dircolors/src/dircolors.rs index 2fa2e8b91..80c94c047 100644 --- a/src/uu/dircolors/src/dircolors.rs +++ b/src/uu/dircolors/src/dircolors.rs @@ -191,24 +191,27 @@ pub trait StrUtils { impl StrUtils for str { fn purify(&self) -> &Self { - let mut line = self; - for (n, c) in self.chars().enumerate() { - if c != '#' { - continue; - } - + let line = if self.as_bytes().first() == Some(&b'#') { // Ignore if '#' is at the beginning of line - if n == 0 { - line = &self[..0]; - break; + &self[..0] + } else { + let mut line = self; + for (n, _) in self + .as_bytes() + .iter() + .enumerate() + .rev() + .filter(|(_, c)| **c == b'#') + { + // Ignore the content after '#' + // only if it is preceded by at least one whitespace + if self[..n].chars().last().unwrap().is_whitespace() { + line = &self[..n]; + break; + } } - - // Ignore the content after '#' - // only if it is preceded by at least one whitespace - if self.chars().nth(n - 1).unwrap().is_whitespace() { - line = &self[..n]; - } - } + line + }; line.trim() } From e3197bea39ca6093126dbdf1d24d834f03e09837 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Thu, 10 Jun 2021 18:14:23 +0900 Subject: [PATCH 02/37] dircolor purify forward match '#' --- src/uu/dircolors/src/dircolors.rs | 37 ++++++++++++++++--------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/uu/dircolors/src/dircolors.rs b/src/uu/dircolors/src/dircolors.rs index 80c94c047..530f051b3 100644 --- a/src/uu/dircolors/src/dircolors.rs +++ b/src/uu/dircolors/src/dircolors.rs @@ -191,27 +191,28 @@ pub trait StrUtils { impl StrUtils for str { fn purify(&self) -> &Self { - let line = if self.as_bytes().first() == Some(&b'#') { - // Ignore if '#' is at the beginning of line - &self[..0] - } else { - let mut line = self; - for (n, _) in self - .as_bytes() - .iter() - .enumerate() - .rev() - .filter(|(_, c)| **c == b'#') - { - // Ignore the content after '#' - // only if it is preceded by at least one whitespace - if self[..n].chars().last().unwrap().is_whitespace() { - line = &self[..n]; + let mut line = self; + for (n, _) in self + .as_bytes() + .iter() + .enumerate() + .filter(|(_, c)| **c == b'#') + { + // Ignore the content after '#' + // only if it is preceded by at least one whitespace + match self[..n].chars().last() { + Some(c) if c.is_whitespace() => { + line = &self[..n - c.len_utf8()]; break; } + None => { + // n == 0 + line = &self[..0]; + break; + } + _ => (), } - line - }; + } line.trim() } From b335e7f2aeb6125e068d614affeed96fa2f11e6c Mon Sep 17 00:00:00 2001 From: Hanif Bin Ariffin Date: Sat, 12 Jun 2021 17:57:35 +0800 Subject: [PATCH 03/37] Now stops at the last line first. Press down again to go to next file or quit Signed-off-by: Hanif Bin Ariffin --- src/uu/more/src/more.rs | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/src/uu/more/src/more.rs b/src/uu/more/src/more.rs index 206cebbc2..d83961d2c 100644 --- a/src/uu/more/src/more.rs +++ b/src/uu/more/src/more.rs @@ -239,7 +239,11 @@ fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>, silent: bo code: KeyCode::Char(' '), modifiers: KeyModifiers::NONE, }) => { - pager.page_down(); + if pager.should_close() { + return; + } else { + pager.page_down(); + } } Event::Key(KeyEvent { code: KeyCode::Up, @@ -253,9 +257,6 @@ fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>, silent: bo } pager.draw(stdout, wrong_key); - if pager.should_close() { - return; - } } } } @@ -268,7 +269,6 @@ struct Pager<'a> { lines: Vec, next_file: Option<&'a str>, line_count: usize, - close_on_down: bool, silent: bool, } @@ -277,33 +277,25 @@ impl<'a> Pager<'a> { let line_count = lines.len(); Self { upper_mark: 0, - content_rows: rows - 1, + content_rows: rows.saturating_sub(1), lines, next_file, line_count, - close_on_down: false, silent, } } fn should_close(&mut self) -> bool { - if self.upper_mark + self.content_rows >= self.line_count { - if self.close_on_down { - return true; - } - if self.next_file.is_none() { - return true; - } else { - self.close_on_down = true; - } - } else { - self.close_on_down = false; - } - false + self.upper_mark + .saturating_add(self.content_rows) + .eq(&self.line_count) } fn page_down(&mut self) { - self.upper_mark += self.content_rows; + self.upper_mark = self + .upper_mark + .saturating_add(self.content_rows) + .min(self.line_count.saturating_sub(self.content_rows)); } fn page_up(&mut self) { @@ -364,7 +356,7 @@ impl<'a> Pager<'a> { // Break the lines on the cols of the terminal fn break_buff(buff: &str, cols: usize) -> Vec { - let mut lines = Vec::new(); + let mut lines = Vec::with_capacity(buff.lines().count()); for l in buff.lines() { lines.append(&mut break_line(l, cols)); From 63ee42826b017ac4d0075dbdfc373535e731d3af Mon Sep 17 00:00:00 2001 From: Hanif Bin Ariffin Date: Sat, 12 Jun 2021 18:02:31 +0800 Subject: [PATCH 04/37] Fixed numeric type 1. Its better to bump u16 to usize than the other way round. 2. Highly unlikely to have a terminal with usize rows...makes making sense of the code easier. Signed-off-by: Hanif Bin Ariffin --- src/uu/more/src/more.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/uu/more/src/more.rs b/src/uu/more/src/more.rs index d83961d2c..90ef07e99 100644 --- a/src/uu/more/src/more.rs +++ b/src/uu/more/src/more.rs @@ -210,7 +210,7 @@ fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>, silent: bo let (cols, rows) = terminal::size().unwrap(); let lines = break_buff(buff, usize::from(cols)); - let mut pager = Pager::new(rows as usize, lines, next_file, silent); + let mut pager = Pager::new(rows, lines, next_file, silent); pager.draw(stdout, false); if pager.should_close() { return; @@ -265,7 +265,7 @@ struct Pager<'a> { // The current line at the top of the screen upper_mark: usize, // The number of rows that fit on the screen - content_rows: usize, + content_rows: u16, lines: Vec, next_file: Option<&'a str>, line_count: usize, @@ -273,7 +273,7 @@ struct Pager<'a> { } impl<'a> Pager<'a> { - fn new(rows: usize, lines: Vec, next_file: Option<&'a str>, silent: bool) -> Self { + fn new(rows: u16, lines: Vec, next_file: Option<&'a str>, silent: bool) -> Self { let line_count = lines.len(); Self { upper_mark: 0, @@ -287,23 +287,25 @@ impl<'a> Pager<'a> { fn should_close(&mut self) -> bool { self.upper_mark - .saturating_add(self.content_rows) + .saturating_add(self.content_rows.into()) .eq(&self.line_count) } fn page_down(&mut self) { self.upper_mark = self .upper_mark - .saturating_add(self.content_rows) - .min(self.line_count.saturating_sub(self.content_rows)); + .saturating_add(self.content_rows.into()) + .min(self.line_count.saturating_sub(self.content_rows.into())); } fn page_up(&mut self) { - self.upper_mark = self.upper_mark.saturating_sub(self.content_rows); + self.upper_mark = self.upper_mark.saturating_sub(self.content_rows.into()); } fn draw(&self, stdout: &mut std::io::Stdout, wrong_key: bool) { - let lower_mark = self.line_count.min(self.upper_mark + self.content_rows); + let lower_mark = self + .line_count + .min(self.upper_mark.saturating_add(self.content_rows.into())); self.draw_lines(stdout); self.draw_prompt(stdout, lower_mark, wrong_key); stdout.flush().unwrap(); @@ -315,7 +317,7 @@ impl<'a> Pager<'a> { .lines .iter() .skip(self.upper_mark) - .take(self.content_rows); + .take(self.content_rows.into()); for line in displayed_lines { stdout From ee6419f11c85056682f4ba190cd9f6329f04043c Mon Sep 17 00:00:00 2001 From: Hanif Bin Ariffin Date: Sat, 12 Jun 2021 18:10:38 +0800 Subject: [PATCH 05/37] Fixing display when resizing terminal Signed-off-by: Hanif Bin Ariffin --- src/uu/more/src/more.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/uu/more/src/more.rs b/src/uu/more/src/more.rs index 90ef07e99..dac48cea7 100644 --- a/src/uu/more/src/more.rs +++ b/src/uu/more/src/more.rs @@ -251,6 +251,10 @@ fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>, silent: bo }) => { pager.page_up(); } + Event::Resize(col, row) => { + pager.page_resize(col, row); + } + // FIXME: Need to fix, there are more than just unknown keys. _ => { wrong_key = true; } @@ -302,6 +306,11 @@ impl<'a> Pager<'a> { self.upper_mark = self.upper_mark.saturating_sub(self.content_rows.into()); } + // TODO: Deal with column size changes. + fn page_resize(&mut self, _: u16, row: u16) { + self.content_rows = row.saturating_sub(1); + } + fn draw(&self, stdout: &mut std::io::Stdout, wrong_key: bool) { let lower_mark = self .line_count From 28c6fad6e3855176ea77b5899be225d9f2eff5ee Mon Sep 17 00:00:00 2001 From: Hanif Bin Ariffin Date: Sat, 12 Jun 2021 18:25:14 +0800 Subject: [PATCH 06/37] Now displays the unknown key entered Signed-off-by: Hanif Bin Ariffin --- src/uu/more/src/more.rs | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/uu/more/src/more.rs b/src/uu/more/src/more.rs index dac48cea7..3724cd801 100644 --- a/src/uu/more/src/more.rs +++ b/src/uu/more/src/more.rs @@ -211,13 +211,13 @@ fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>, silent: bo let lines = break_buff(buff, usize::from(cols)); let mut pager = Pager::new(rows, lines, next_file, silent); - pager.draw(stdout, false); + pager.draw(stdout, None); if pager.should_close() { return; } loop { - let mut wrong_key = false; + let mut wrong_key = None; if event::poll(Duration::from_millis(10)).unwrap() { match event::read().unwrap() { Event::Key(KeyEvent { @@ -254,10 +254,11 @@ fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>, silent: bo Event::Resize(col, row) => { pager.page_resize(col, row); } - // FIXME: Need to fix, there are more than just unknown keys. - _ => { - wrong_key = true; - } + Event::Key(KeyEvent { + code: KeyCode::Char(k), + .. + }) => wrong_key = Some(k), + _ => continue, } pager.draw(stdout, wrong_key); @@ -311,7 +312,7 @@ impl<'a> Pager<'a> { self.content_rows = row.saturating_sub(1); } - fn draw(&self, stdout: &mut std::io::Stdout, wrong_key: bool) { + fn draw(&self, stdout: &mut std::io::Stdout, wrong_key: Option) { let lower_mark = self .line_count .min(self.upper_mark.saturating_add(self.content_rows.into())); @@ -335,7 +336,7 @@ impl<'a> Pager<'a> { } } - fn draw_prompt(&self, stdout: &mut Stdout, lower_mark: usize, wrong_key: bool) { + fn draw_prompt(&self, stdout: &mut Stdout, lower_mark: usize, wrong_key: Option) { let status_inner = if lower_mark == self.line_count { format!("Next file: {}", self.next_file.unwrap_or_default()) } else { @@ -348,10 +349,15 @@ impl<'a> Pager<'a> { let status = format!("--More--({})", status_inner); let banner = match (self.silent, wrong_key) { - (true, true) => "[Press 'h' for instructions. (unimplemented)]".to_string(), - (true, false) => format!("{}[Press space to continue, 'q' to quit.]", status), - (false, true) => format!("{}{}", status, BELL), - (false, false) => status, + (true, Some(key)) => { + format!( + "{} [Unknown key: '{}'. Press 'h' for instructions. (unimplemented)]", + status, key + ) + } + (true, None) => format!("{}[Press space to continue, 'q' to quit.]", status), + (false, Some(_)) => format!("{}{}", status, BELL), + (false, None) => status, }; write!( From 9ed5091be6bf4f0f9c13ec85a1b1ea9f72c4eccb Mon Sep 17 00:00:00 2001 From: Hanif Bin Ariffin Date: Sat, 12 Jun 2021 20:30:15 +0800 Subject: [PATCH 07/37] Fixed hanging with smaller content Using 'seq 10 | cargo run -- more' should no longer hangs. Signed-off-by: Hanif Bin Ariffin --- src/uu/more/src/more.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/more/src/more.rs b/src/uu/more/src/more.rs index 3724cd801..93a2f0edf 100644 --- a/src/uu/more/src/more.rs +++ b/src/uu/more/src/more.rs @@ -293,7 +293,7 @@ impl<'a> Pager<'a> { fn should_close(&mut self) -> bool { self.upper_mark .saturating_add(self.content_rows.into()) - .eq(&self.line_count) + .ge(&self.line_count) } fn page_down(&mut self) { From 083e74597613d2edd1d17fce1bca282a8db0f315 Mon Sep 17 00:00:00 2001 From: Hanif Bin Ariffin Date: Sat, 12 Jun 2021 20:34:21 +0800 Subject: [PATCH 08/37] Simplified page down implementation Signed-off-by: Hanif Bin Ariffin --- src/uu/more/src/more.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/uu/more/src/more.rs b/src/uu/more/src/more.rs index 93a2f0edf..d7fba5080 100644 --- a/src/uu/more/src/more.rs +++ b/src/uu/more/src/more.rs @@ -297,10 +297,7 @@ impl<'a> Pager<'a> { } fn page_down(&mut self) { - self.upper_mark = self - .upper_mark - .saturating_add(self.content_rows.into()) - .min(self.line_count.saturating_sub(self.content_rows.into())); + self.upper_mark = self.upper_mark.saturating_add(self.content_rows.into()); } fn page_up(&mut self) { From 285eeac1fb88d1d3b354ec77f73375546fd72e75 Mon Sep 17 00:00:00 2001 From: Michael Debertol Date: Fri, 18 Jun 2021 18:49:39 +0200 Subject: [PATCH 09/37] tests/pr: include one more possible minute --- tests/by-util/test_pr.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/by-util/test_pr.rs b/tests/by-util/test_pr.rs index fb6703f28..4a79a3eda 100644 --- a/tests/by-util/test_pr.rs +++ b/tests/by-util/test_pr.rs @@ -22,6 +22,7 @@ fn file_last_modified_time(ucmd: &UCommand, path: &str) -> String { } fn all_minutes(from: DateTime, to: DateTime) -> Vec { + let to = to + Duration::minutes(1); const FORMAT: &str = "%b %d %H:%M %Y"; let mut vec = vec![]; let mut current = from; From 7b9814c7784dd93c31df03e12038ada37082dd55 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Fri, 18 Jun 2021 16:56:00 +0300 Subject: [PATCH 10/37] test: Implement [ expr ] syntax When invoked via '[' name, last argument must be ']' or we bail out with syntax error. Then the trailing ']' is simply disregarded and processing happens like usual. --- build.rs | 23 +++++++++++++++++++++++ src/uu/test/src/test.rs | 20 +++++++++++++++++--- tests/by-util/test_test.rs | 28 ++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/build.rs b/build.rs index ae38177b0..2ed8e1345 100644 --- a/build.rs +++ b/build.rs @@ -54,6 +54,29 @@ pub fn main() { for krate in crates { match krate.as_ref() { + // 'test' is named uu_test to avoid collision with rust core crate 'test'. + // It can also be invoked by name '[' for the '[ expr ] syntax'. + "uu_test" => { + mf.write_all( + format!( + "\ + \tmap.insert(\"test\", {krate}::uumain);\n\ + \t\tmap.insert(\"[\", {krate}::uumain);\n\ + ", + krate = krate + ) + .as_bytes(), + ) + .unwrap(); + tf.write_all( + format!( + "#[path=\"{dir}/test_test.rs\"]\nmod test_test;\n", + dir = util_tests_dir, + ) + .as_bytes(), + ) + .unwrap() + } k if k.starts_with(override_prefix) => { mf.write_all( format!( diff --git a/src/uu/test/src/test.rs b/src/uu/test/src/test.rs index 5f20b95f0..97a244cdc 100644 --- a/src/uu/test/src/test.rs +++ b/src/uu/test/src/test.rs @@ -12,10 +12,24 @@ mod parser; use parser::{parse, Symbol}; use std::ffi::{OsStr, OsString}; +use std::path::Path; -pub fn uumain(args: impl uucore::Args) -> i32 { - // TODO: handle being called as `[` - let args: Vec<_> = args.skip(1).collect(); +pub fn uumain(mut args: impl uucore::Args) -> i32 { + let program = args.next().unwrap_or_else(|| OsString::from("test")); + let binary_name = Path::new(&program) + .file_name() + .unwrap_or_else(|| OsStr::new("test")) + .to_string_lossy(); + let mut args: Vec<_> = args.collect(); + + // If invoked via name '[', matching ']' must be in the last arg + if binary_name == "[" { + let last = args.pop(); + if last != Some(OsString::from("]")) { + eprintln!("[: missing ']'"); + return 2; + } + } let result = parse(args).and_then(|mut stack| eval(&mut stack)); diff --git a/tests/by-util/test_test.rs b/tests/by-util/test_test.rs index c4964d6bf..36e825f2d 100644 --- a/tests/by-util/test_test.rs +++ b/tests/by-util/test_test.rs @@ -690,3 +690,31 @@ fn test_or_as_filename() { fn test_string_length_and_nothing() { new_ucmd!().args(&["-n", "a", "-a"]).run().status_code(2); } + +#[test] +fn test_bracket_syntax_success() { + let scenario = TestScenario::new("["); + let mut ucmd = scenario.ucmd(); + + ucmd.args(&["1", "-eq", "1", "]"]).succeeds(); +} + +#[test] +fn test_bracket_syntax_failure() { + let scenario = TestScenario::new("["); + let mut ucmd = scenario.ucmd(); + + ucmd.args(&["1", "-eq", "2", "]"]).run().status_code(1); +} + +#[test] +fn test_bracket_syntax_missing_right_bracket() { + let scenario = TestScenario::new("["); + let mut ucmd = scenario.ucmd(); + + // Missing closing bracket takes precedence over other possible errors. + ucmd.args(&["1", "-eq"]) + .run() + .status_code(2) + .stderr_is("[: missing ']'"); +} From 6400cded54ba436ad72f0758dfafe174491e378e Mon Sep 17 00:00:00 2001 From: Michael Debertol Date: Sat, 19 Jun 2021 17:45:45 +0200 Subject: [PATCH 11/37] cp: fix order of checks in `copy_helper` --- src/uu/cp/src/cp.rs | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 851117bde..cf723e4ee 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1218,28 +1218,38 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> { /// Copy the file from `source` to `dest` either using the normal `fs::copy` or a /// copy-on-write scheme if --reflink is specified and the filesystem supports it. fn copy_helper(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> { - if options.reflink_mode != ReflinkMode::Never { - #[cfg(not(any(target_os = "linux", target_os = "macos")))] - return Err("--reflink is only supported on linux and macOS" - .to_string() - .into()); - - #[cfg(target_os = "macos")] - copy_on_write_macos(source, dest, options.reflink_mode)?; - #[cfg(target_os = "linux")] - copy_on_write_linux(source, dest, options.reflink_mode)?; - } else if !options.dereference && fs::symlink_metadata(&source)?.file_type().is_symlink() { - copy_link(source, dest)?; - } else if source.to_string_lossy() == "/dev/null" { + if options.parents { + let parent = dest.parent().unwrap_or(dest); + fs::create_dir_all(parent)?; + } + let is_symlink = fs::symlink_metadata(&source)?.file_type().is_symlink(); + if source.to_string_lossy() == "/dev/null" { /* workaround a limitation of fs::copy * https://github.com/rust-lang/rust/issues/79390 */ File::create(dest)?; - } else { - if options.parents { - let parent = dest.parent().unwrap_or(dest); - fs::create_dir_all(parent)?; + } else if !options.dereference && is_symlink { + copy_link(source, dest)?; + } else if options.reflink_mode != ReflinkMode::Never { + #[cfg(not(any(target_os = "linux", target_os = "macos")))] + return Err("--reflink is only supported on linux and macOS" + .to_string() + .into()); + if is_symlink { + assert!(options.dereference); + let real_path = std::fs::read_link(source)?; + + #[cfg(target_os = "macos")] + copy_on_write_macos(&real_path, dest, options.reflink_mode)?; + #[cfg(target_os = "linux")] + copy_on_write_linux(&real_path, dest, options.reflink_mode)?; + } else { + #[cfg(target_os = "macos")] + copy_on_write_macos(source, dest, options.reflink_mode)?; + #[cfg(target_os = "linux")] + copy_on_write_linux(source, dest, options.reflink_mode)?; } + } else { fs::copy(source, dest).context(&*context_for(source, dest))?; } From 9fb927aa856eec92a58b6cfbf53438e4a48f71e2 Mon Sep 17 00:00:00 2001 From: Michael Debertol Date: Sat, 19 Jun 2021 17:49:04 +0200 Subject: [PATCH 12/37] cp: always delete the destination for symlinks --- src/uu/cp/src/cp.rs | 5 +++++ tests/by-util/test_cp.rs | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index cf723e4ee..840035e4a 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1269,6 +1269,11 @@ fn copy_link(source: &Path, dest: &Path) -> CopyResult<()> { ), } } else { + // we always need to remove the file to be able to create a symlink, + // even if it is writeable. + if dest.exists() { + fs::remove_file(dest)?; + } dest.into() }; symlink_file(&link, &dest, &*context_for(&link, &dest)) diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 4ce587e02..19f93e499 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -1325,3 +1325,16 @@ fn test_copy_dir_with_symlinks() { ucmd.args(&["-r", "dir", "copy"]).succeeds(); assert_eq!(at.resolve_link("copy/file-link"), "file"); } + +#[test] +#[cfg(not(windows))] +fn test_copy_symlink_force() { + let (at, mut ucmd) = at_and_ucmd!(); + at.touch("file"); + at.symlink_file("file", "file-link"); + at.touch("copy"); + + ucmd.args(&["file-link", "copy", "-f", "--no-dereference"]) + .succeeds(); + assert_eq!(at.resolve_link("copy"), "file"); +} From 076c7fa501d43a190ab4356dfaa583677c80dc3b Mon Sep 17 00:00:00 2001 From: Michael Debertol Date: Sat, 19 Jun 2021 17:49:41 +0200 Subject: [PATCH 13/37] cp: default to --reflink=auto on linux and macos --- src/uu/cp/src/cp.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 840035e4a..7cf6a1d9b 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -667,7 +667,14 @@ impl Options { } } } else { - ReflinkMode::Never + #[cfg(any(target_os = "linux", target_os = "macos"))] + { + ReflinkMode::Auto + } + #[cfg(not(any(target_os = "linux", target_os = "macos")))] + { + ReflinkMode::Never + } } }, backup: backup_mode, From 90bf26a51c805ec0d8f776ff47f98e4e3e6bb1b1 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 6 Jun 2021 20:21:23 +0200 Subject: [PATCH 14/37] maint/CICD ~ (GHA) update to checkout@v2 --- .github/workflows/CICD.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index fcaddd310..65051a88e 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -26,7 +26,7 @@ jobs: job: - { os: ubuntu-latest , features: feat_os_unix } steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Initialize workflow variables id: vars shell: bash @@ -66,7 +66,7 @@ jobs: job: - { os: ubuntu-latest } steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Install/setup prerequisites shell: bash run: | @@ -87,7 +87,7 @@ jobs: - { os: macos-latest , features: feat_os_macos } - { os: windows-latest , features: feat_os_windows } steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Initialize workflow variables id: vars shell: bash @@ -122,7 +122,7 @@ jobs: job: - { os: ubuntu-latest , features: feat_os_unix } steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Install `rust` toolchain (v${{ env.RUST_MIN_SRV }}) uses: actions-rs/toolchain@v1 with: @@ -181,7 +181,7 @@ jobs: job: - { os: ubuntu-latest } steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Install `rust` toolchain uses: actions-rs/toolchain@v1 with: @@ -212,7 +212,7 @@ jobs: job: - { os: ubuntu-latest } steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Install `rust` toolchain uses: actions-rs/toolchain@v1 with: @@ -249,7 +249,7 @@ jobs: - { os: windows-latest , target: x86_64-pc-windows-gnu , features: feat_os_windows } ## note: requires rust >= 1.43.0 to link correctly - { os: windows-latest , target: x86_64-pc-windows-msvc , features: feat_os_windows } steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Install/setup prerequisites shell: bash run: | @@ -488,7 +488,7 @@ jobs: - { os: macos-latest , features: macos } - { os: windows-latest , features: windows } steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Install/setup prerequisites shell: bash run: | From 298851096ef941a3d9658002e5b9eac4f8fb1bea Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Sat, 12 Jun 2021 21:35:11 -0500 Subject: [PATCH 15/37] maint/CICD ~ (GHA) remove deprecated 'ubuntu-16.04' environment --- .github/workflows/CICD.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 65051a88e..2667169dd 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -235,8 +235,6 @@ jobs: # { os, target, cargo-options, features, use-cross, toolchain } - { os: ubuntu-latest , target: arm-unknown-linux-gnueabihf , features: feat_os_unix_gnueabihf , use-cross: use-cross } - { os: ubuntu-latest , target: aarch64-unknown-linux-gnu , features: feat_os_unix_gnueabihf , use-cross: use-cross } - - { os: ubuntu-latest , target: x86_64-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross } - - { os: ubuntu-16.04 , target: x86_64-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross } # - { os: ubuntu-18.04 , target: i586-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross } ## note: older windows platform; not required, dev-FYI only # - { os: ubuntu-18.04 , target: i586-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross } ## note: older windows platform; not required, dev-FYI only - { os: ubuntu-18.04 , target: i686-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross } From db621c7d7a0a8a23b2fedb0eac5deefa0b74198a Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Sat, 12 Jun 2021 13:46:37 -0500 Subject: [PATCH 16/37] maint/CICD ~ (GHA) change/refactor CICD (convert most warnings to errors) - adds additional instruction to error message showing how to fix the error --- .github/workflows/CICD.yml | 167 +++++++++++++++++++------------------ 1 file changed, 88 insertions(+), 79 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 2667169dd..48df4e546 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -48,36 +48,19 @@ jobs: - name: "`fmt` testing" shell: bash run: | - # `fmt` testing + ## `fmt` testing # * convert any warnings to GHA UI annotations; ref: - S=$(cargo fmt -- --check) && printf "%s\n" "$S" || { printf "%s\n" "$S" | sed -E -n -e "s/^Diff[[:space:]]+in[[:space:]]+${PWD//\//\\/}\/(.*)[[:space:]]+at[[:space:]]+[^0-9]+([0-9]+).*$/::warning file=\1,line=\2::WARNING: \`cargo fmt\`: style violation/p" ; } + S=$(cargo fmt -- --check) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s\n" "$S" | sed -E -n -e "s/^Diff[[:space:]]+in[[:space:]]+${PWD//\//\\/}\/(.*)[[:space:]]+at[[:space:]]+[^0-9]+([0-9]+).*$/::error file=\1,line=\2::ERROR: \`cargo fmt\`: style violation (file:'\1', line:\2; use \`cargo fmt \"\1\"\`)/p" ; exit 1 ; } - name: "`fmt` testing of tests" + if: success() || failure() # run regardless of prior step success/failure shell: bash run: | - # `fmt` testing of tests + ## `fmt` testing of tests # * convert any warnings to GHA UI annotations; ref: - S=$(find tests -name "*.rs" -print0 | xargs -0 cargo fmt -- --check) && printf "%s\n" "$S" || { printf "%s\n" "$S" | sed -E -n "s/^Diff[[:space:]]+in[[:space:]]+${PWD//\//\\/}\/(.*)[[:space:]]+at[[:space:]]+[^0-9]+([0-9]+).*$/::warning file=\1,line=\2::WARNING: \`cargo fmt\`: style violation/p" ; } + S=$(find tests -name "*.rs" -print0 | xargs -0 cargo fmt -- --check) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s\n" "$S" | sed -E -n "s/^Diff[[:space:]]+in[[:space:]]+${PWD//\//\\/}\/(.*)[[:space:]]+at[[:space:]]+[^0-9]+([0-9]+).*$/::error file=\1,line=\2::ERROR: \`cargo fmt\`: style violation (file:'\1', line:\2; use \`cargo fmt \"\1\"\`)/p" ; exit 1 ; } - code_spellcheck: - name: Style/spelling - runs-on: ${{ matrix.job.os }} - strategy: - matrix: - job: - - { os: ubuntu-latest } - steps: - - uses: actions/checkout@v2 - - name: Install/setup prerequisites - shell: bash - run: | - sudo apt-get -y update ; sudo apt-get -y install npm ; sudo npm install cspell -g; - - name: Run `cspell` - shell: bash - run: | - cspell --config .vscode/cSpell.json --no-summary --no-progress "**/*" | sed "s/\(.*\):\(.*\):\(.*\) - \(.*\)/::warning file=\1,line=\2,col=\3::cspell: \4/" || true - - code_warnings: - name: Style/warnings + code_lint: + name: Style/lint runs-on: ${{ matrix.job.os }} strategy: fail-fast: false @@ -106,13 +89,32 @@ jobs: default: true profile: minimal # minimal component installation (ie, no documentation) components: clippy - - name: "`clippy` testing" - if: success() || failure() # run regardless of prior step success/failure + - name: "`clippy` lint testing" shell: bash run: | - # `clippy` testing + ## `clippy` lint testing # * convert any warnings to GHA UI annotations; ref: - S=$(cargo +nightly clippy --all-targets ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} -- -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::warning file=\2,line=\3,col=\4::WARNING: \`cargo clippy\`: \1/p;" -e '}' ; } + S=$(cargo +nightly clippy --all-targets ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} -- -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+${PWD//\//\\/}\/(.*):([0-9]+):([0-9]+).*$/::error file=\2,line=\3,col=\4::ERROR: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; exit 1 ; } + + code_spellcheck: + name: Style/spelling + runs-on: ${{ matrix.job.os }} + strategy: + matrix: + job: + - { os: ubuntu-latest } + steps: + - uses: actions/checkout@v2 + - name: Install/setup prerequisites + shell: bash + run: | + ## Install/setup prerequisites + sudo apt-get -y update ; sudo apt-get -y install npm ; sudo npm install cspell -g ; + - name: Run `cspell` + shell: bash + run: | + ## Run `cspell` + cspell --config .vscode/cSpell.json --no-summary --no-progress "**/*" | sed -E -n "s/${PWD//\//\\/}\/(.*):(.*):(.*) - (.*)/::error file=\1,line=\2,col=\3::ERROR: \4 (file:'\1', line:\2)/p" min_version: name: MinRustV # Minimum supported rust version @@ -137,20 +139,20 @@ jobs: use-tool-cache: true env: RUSTUP_TOOLCHAIN: stable - - name: Confirm compatible 'Cargo.lock' + - name: Confirm MinSRV compatible 'Cargo.lock' shell: bash run: | - # Confirm compatible 'Cargo.lock' + ## Confirm MinSRV compatible 'Cargo.lock' # * 'Cargo.lock' is required to be in a format that `cargo` of MinSRV can interpret (eg, v1-format for MinSRV < v1.38) - cargo fetch --locked --quiet || { echo "::error file=Cargo.lock::Incompatible 'Cargo.lock' format; try \`cargo +${{ env.RUST_MIN_SRV }} update\`" ; exit 1 ; } + cargo fetch --locked --quiet || { echo "::error file=Cargo.lock::Incompatible (or out-of-date) 'Cargo.lock' file; update using \`cargo +${{ env.RUST_MIN_SRV }} update\`" ; exit 1 ; } - name: Info shell: bash run: | - # Info - ## environment + ## Info + # environment echo "## environment" echo "CI='${CI}'" - ## tooling info display + # tooling info display echo "## tooling" which gcc >/dev/null 2>&1 && (gcc --version | head -1) || true rustup -V @@ -158,12 +160,11 @@ jobs: cargo -V rustc -V cargo-tree tree -V - ## dependencies + # dependencies echo "## dependency list" cargo fetch --locked --quiet ## * using the 'stable' toolchain is necessary to avoid "unexpected '--filter-platform'" errors RUSTUP_TOOLCHAIN=stable cargo-tree tree --frozen --all --no-dev-dependencies --no-indent --features ${{ matrix.job.features }} | grep -vE "$PWD" | sort --unique - - name: Test uses: actions-rs/cargo@v1 with: @@ -172,8 +173,8 @@ jobs: env: RUSTFLAGS: '-Awarnings' - busybox_test: - name: Busybox test suite + build_makefile: + name: Build/Makefile runs-on: ${{ matrix.job.os }} strategy: fail-fast: false @@ -188,42 +189,19 @@ jobs: toolchain: stable default: true profile: minimal # minimal component installation (ie, no documentation) - - name: "prepare busytest" + - name: Install/setup prerequisites shell: bash run: | - make prepare-busytest - - name: "run busybox testsuite" + ## Install/setup prerequisites + sudo apt-get -y update ; sudo apt-get -y install python3-sphinx ; + - name: "`make build`" shell: bash run: | - bindir=$(pwd)/target/debug - cd tmp/busybox-*/testsuite - ## S=$(bindir=$bindir ./runtest) && printf "%s\n" "$S" || { printf "%s\n" "$S" | grep "FAIL:" | sed -e "s/FAIL: /::warning ::Test failure:/g" ; } - output=$(bindir=$bindir ./runtest 2>&1 || true) - printf "%s\n" "${output}" - n_fails=$(echo "$output" | grep "^FAIL:\s" | wc --lines) - if [ $n_fails -gt 0 ] ; then echo "::warning ::${n_fails}+ test failures" ; fi - - makefile_build: - name: Test the build target of the Makefile - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - job: - - { os: ubuntu-latest } - steps: - - uses: actions/checkout@v2 - - name: Install `rust` toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - default: true - profile: minimal # minimal component installation (ie, no documentation) - - name: "Run make build" - shell: bash - run: | - sudo apt-get -y update ; sudo apt-get -y install python3-sphinx; make build + - name: "`make test`" + shell: bash + run: | + make test build: name: Build @@ -251,7 +229,7 @@ jobs: - name: Install/setup prerequisites shell: bash run: | - ## install/setup prerequisites + ## Install/setup prerequisites case '${{ matrix.job.target }}' in arm-unknown-linux-gnueabihf) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;; aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;; @@ -350,7 +328,7 @@ jobs: - name: Create all needed build/work directories shell: bash run: | - ## create build/work space + ## Create build/work space mkdir -p '${{ steps.vars.outputs.STAGING }}' mkdir -p '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}' mkdir -p '${{ steps.vars.outputs.STAGING }}/dpkg' @@ -387,15 +365,15 @@ jobs: - name: Info shell: bash run: | - # Info - ## commit info + ## Info + # commit info echo "## commit" echo GITHUB_REF=${GITHUB_REF} echo GITHUB_SHA=${GITHUB_SHA} - ## environment + # environment echo "## environment" echo "CI='${CI}'" - ## tooling info display + # tooling info display echo "## tooling" which gcc >/dev/null 2>&1 && (gcc --version | head -1) || true rustup -V @@ -403,7 +381,7 @@ jobs: cargo -V rustc -V cargo-tree tree -V - ## dependencies + # dependencies echo "## dependency list" cargo fetch --locked --quiet cargo-tree tree --target=${{ matrix.job.target }} ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --all --no-dev-dependencies --no-indent | grep -vE "$PWD" | sort --unique @@ -433,7 +411,7 @@ jobs: - name: Package shell: bash run: | - ## package artifact(s) + ## Package artifact(s) # binary cp 'target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}' '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/' # `strip` binary (if needed) @@ -474,6 +452,37 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + test_busybox: + name: Tests/BusyBox test suite + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + job: + - { os: ubuntu-latest } + steps: + - uses: actions/checkout@v2 + - name: Install `rust` toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + default: true + profile: minimal # minimal component installation (ie, no documentation) + - name: Install/setup prerequisites + shell: bash + run: | + make prepare-busytest + - name: "Run BusyBox test suite" + shell: bash + run: | + ## Run BusyBox test suite + bindir=$(pwd)/target/debug + cd tmp/busybox-*/testsuite + output=$(bindir=$bindir ./runtest 2>&1 || true) + printf "%s\n" "${output}" + n_fails=$(echo "$output" | grep "^FAIL:\s" | wc --lines) + if [ $n_fails -gt 0 ] ; then echo "::warning ::${n_fails}+ test failures" ; fi + coverage: name: Code Coverage runs-on: ${{ matrix.job.os }} @@ -490,7 +499,7 @@ jobs: - name: Install/setup prerequisites shell: bash run: | - ## install/setup prerequisites + ## Install/setup prerequisites case '${{ matrix.job.os }}' in macos-latest) brew install coreutils ;; # needed for testing esac @@ -584,7 +593,7 @@ jobs: id: coverage shell: bash run: | - # generate coverage data + ## Generate coverage data COVERAGE_REPORT_DIR="target/debug" COVERAGE_REPORT_FILE="${COVERAGE_REPORT_DIR}/lcov.info" # GRCOV_IGNORE_OPTION='--ignore build.rs --ignore "/*" --ignore "[a-zA-Z]:/*"' ## `grcov` ignores these params when passed as an environment variable (why?) From 92630a06904072a4db064b25b33366cfe9bee2c1 Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Sat, 12 Jun 2021 19:16:06 -0500 Subject: [PATCH 17/37] maint/CICD ~ (GHA) add 'Style/dependencies' checks --- .github/workflows/CICD.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 48df4e546..9d2edeac1 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -17,6 +17,40 @@ env: on: [push, pull_request] jobs: + code_deps: + name: Style/dependencies + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + job: + - { os: ubuntu-latest , features: feat_os_unix } + steps: + - uses: actions/checkout@v2 + - name: Initialize workflow variables + id: vars + shell: bash + run: | + ## VARs setup + outputs() { for var in "$@" ; do echo steps.vars.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } + # target-specific options + # * CARGO_FEATURES_OPTION + CARGO_FEATURES_OPTION='' ; + if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi + outputs CARGO_FEATURES_OPTION + - name: Install `rust` toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + default: true + profile: minimal # minimal component installation (ie, no documentation) + - name: "`cargo update` testing" + shell: bash + run: | + ## `cargo update` testing + # * convert any warnings to GHA UI annotations; ref: + cargo fetch --locked --quiet || { echo "::error file=Cargo.lock::'Cargo.lock' file requires update (use \`cargo +${{ env.RUST_MIN_SRV }} update\`)" ; exit 1 ; } + code_format: name: Style/format runs-on: ${{ matrix.job.os }} From 5682cf30320f144c6e11fff8371767ba47974c9a Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Sat, 12 Jun 2021 17:34:48 -0500 Subject: [PATCH 18/37] maint/CICD ~ (GHA) update 'GNU' workflow - show dashboard warnings only when tests FAIL or ERROR - improve comments - fix spelling and spelling exceptions --- .github/workflows/GNU.yml | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/workflows/GNU.yml b/.github/workflows/GNU.yml index 1f9250900..7ed5f4911 100644 --- a/.github/workflows/GNU.yml +++ b/.github/workflows/GNU.yml @@ -1,5 +1,7 @@ name: GNU +# spell-checker:ignore (names) gnulib ; (utils) autopoint gperf pyinotify texinfo ; (vars) XPASS + on: [push, pull_request] jobs: @@ -7,7 +9,6 @@ jobs: name: Run GNU tests runs-on: ubuntu-latest steps: - # Checks out a copy of your repository on the ubuntu-latest machine - name: Checkout code uutil uses: actions/checkout@v2 with: @@ -18,7 +19,7 @@ jobs: repository: 'coreutils/coreutils' path: 'gnu' ref: v8.32 - - name: Checkout GNU corelib + - name: Checkout GNU coreutils library (gnulib) uses: actions/checkout@v2 with: repository: 'coreutils/gnulib' @@ -32,23 +33,26 @@ jobs: default: true profile: minimal # minimal component installation (ie, no documentation) components: rustfmt - - name: Install deps + - name: Install dependencies shell: bash run: | + ## Install dependencies sudo apt-get update sudo apt-get install autoconf autopoint bison texinfo gperf gcc g++ gdb python-pyinotify python3-sphinx jq - name: Build binaries shell: bash run: | - cd uutils - bash util/build-gnu.sh + ## Build binaries + cd uutils + bash util/build-gnu.sh - name: Run GNU tests shell: bash run: | bash uutils/util/run-gnu-test.sh - - name: Extract tests info + - name: Extract testing info shell: bash run: | + ## Extract testing info LOG_FILE=gnu/tests/test-suite.log if test -f "$LOG_FILE" then @@ -58,7 +62,9 @@ jobs: FAIL=$(sed -n "s/.*# FAIL: \(.*\)/\1/p" "$LOG_FILE"|tr -d '\r'|head -n1) XPASS=$(sed -n "s/.*# XPASS: \(.*\)/\1/p" "$LOG_FILE"|tr -d '\r'|head -n1) ERROR=$(sed -n "s/.*# ERROR: \(.*\)/\1/p" "$LOG_FILE"|tr -d '\r'|head -n1) - echo "::warning ::GNU testsuite = TOTAL: $TOTAL / PASS: $PASS / FAIL: $FAIL / ERROR: $ERROR" + output="GNU tests summary = TOTAL: $TOTAL / PASS: $PASS / FAIL: $FAIL / ERROR: $ERROR" + echo "${output}" + if [[ "$FAIL" -gt 0 || "$ERROR" -gt 0 ]]; then echo "::warning ::${output}" ; fi jq -n \ --arg date "$(date --rfc-email)" \ --arg sha "$GITHUB_SHA" \ @@ -72,12 +78,10 @@ jobs: else echo "::error ::Failed to get summary of test results" fi - - uses: actions/upload-artifact@v2 with: name: test-report path: gnu/tests/**/*.log - - uses: actions/upload-artifact@v2 with: name: gnu-result From dd46c2f03b3c085828cbafc9e01ad63f5d06d061 Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Sat, 12 Jun 2021 23:37:00 -0500 Subject: [PATCH 19/37] maint/CICD ~ (GHA) rename 'GNU' workflow to 'GnuTests' --- .github/workflows/{GNU.yml => GnuTests.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{GNU.yml => GnuTests.yml} (99%) diff --git a/.github/workflows/GNU.yml b/.github/workflows/GnuTests.yml similarity index 99% rename from .github/workflows/GNU.yml rename to .github/workflows/GnuTests.yml index 7ed5f4911..90af6a689 100644 --- a/.github/workflows/GNU.yml +++ b/.github/workflows/GnuTests.yml @@ -1,4 +1,4 @@ -name: GNU +name: GnuTests # spell-checker:ignore (names) gnulib ; (utils) autopoint gperf pyinotify texinfo ; (vars) XPASS From c171b13982dfae75e6e53f2c76934e0c37e94762 Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Sat, 12 Jun 2021 19:50:40 -0500 Subject: [PATCH 20/37] docs/spell ~ update cspell dictionaries --- .vscode/cspell.dictionaries/acronyms+names.wordlist.txt | 2 ++ .vscode/cspell.dictionaries/jargon.wordlist.txt | 1 + .vscode/cspell.dictionaries/workspace.wordlist.txt | 8 +++++--- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.vscode/cspell.dictionaries/acronyms+names.wordlist.txt b/.vscode/cspell.dictionaries/acronyms+names.wordlist.txt index 3956d1d8a..a46448a32 100644 --- a/.vscode/cspell.dictionaries/acronyms+names.wordlist.txt +++ b/.vscode/cspell.dictionaries/acronyms+names.wordlist.txt @@ -12,6 +12,7 @@ FIFOs FQDN # fully qualified domain name GID # group ID GIDs +GNU GNUEABI GNUEABIhf JFS @@ -45,6 +46,7 @@ Deno EditorConfig FreeBSD Gmail +GNU Irix MS-DOS MSDOS diff --git a/.vscode/cspell.dictionaries/jargon.wordlist.txt b/.vscode/cspell.dictionaries/jargon.wordlist.txt index 89af1b153..c2e2c29f3 100644 --- a/.vscode/cspell.dictionaries/jargon.wordlist.txt +++ b/.vscode/cspell.dictionaries/jargon.wordlist.txt @@ -78,6 +78,7 @@ symlinks syscall syscalls tokenize +toolchain truthy unbuffered unescape diff --git a/.vscode/cspell.dictionaries/workspace.wordlist.txt b/.vscode/cspell.dictionaries/workspace.wordlist.txt index ed634dffb..7242199a5 100644 --- a/.vscode/cspell.dictionaries/workspace.wordlist.txt +++ b/.vscode/cspell.dictionaries/workspace.wordlist.txt @@ -48,17 +48,19 @@ xattr # * rust/rustc RUSTDOCFLAGS RUSTFLAGS +clippy +rustc +rustfmt +rustup +# bitor # BitOr trait function bitxor # BitXor trait function -clippy concat fract powi println repr rfind -rustc -rustfmt struct structs substr From b11e9a057e3c08aa3edeffea4dd6d6e5e2064d8a Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Sat, 12 Jun 2021 19:52:34 -0500 Subject: [PATCH 21/37] docs/spell ~ (uucore) add spelling exceptions --- src/uucore/src/lib/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uucore/src/lib/lib.rs b/src/uucore/src/lib/lib.rs index f765b7b3e..bf2e5b1bb 100644 --- a/src/uucore/src/lib/lib.rs +++ b/src/uucore/src/lib/lib.rs @@ -186,7 +186,7 @@ mod tests { fn make_os_vec(os_str: &OsStr) -> Vec { vec![ OsString::from("test"), - OsString::from("สวัสดี"), + OsString::from("สวัสดี"), // spell-checker:disable-line os_str.to_os_string(), ] } From 2cb97c81ed9b214f718c3c6ec64210064be296f0 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 6 Jun 2021 20:24:40 +0200 Subject: [PATCH 22/37] maint/CICD ~ add GHA 'FixPR' to auto-fix issues for merging PRs - auto-fix formatting - auto-fix incompatible/out-of-date 'Cargo.lock' --- .github/workflows/FixPR.yml | 133 ++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 .github/workflows/FixPR.yml diff --git a/.github/workflows/FixPR.yml b/.github/workflows/FixPR.yml new file mode 100644 index 000000000..17470df26 --- /dev/null +++ b/.github/workflows/FixPR.yml @@ -0,0 +1,133 @@ +name: FixPR + +# Trigger automated fixes for PRs being merged (with associated commits) + +env: + BRANCH_TARGET: master + +on: + # * only trigger on pull request closed to specific branches + # ref: https://github.community/t/trigger-workflow-only-on-pull-request-merge/17359/9 + pull_request: + branches: + - master # == env.BRANCH_TARGET ## unfortunately, env context variables are only available in jobs/steps (see ) + types: [ closed ] + +jobs: + code_deps: + # Refresh dependencies (ie, 'Cargo.lock') and show updated dependency tree + if: github.event.pull_request.merged == true ## only for PR merges + name: Update/dependencies + runs-on: ${{ matrix.job.os }} + strategy: + matrix: + job: + - { os: ubuntu-latest , features: feat_os_unix } + steps: + - uses: actions/checkout@v2 + - name: Initialize job variables + id: vars + shell: bash + run: | + ## VARs setup + outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } + # surface MSRV from CICD workflow + RUST_MIN_SRV=$(grep -P "^\s+RUST_MIN_SRV:" .github/workflows/CICD.yml | grep -Po "(?<=\x22)\d+[.]\d+(?:[.]\d+)?(?=\x22)" ) + outputs RUST_MIN_SRV + - name: Install `rust` toolchain (v${{ steps.vars.outputs.RUST_MIN_SRV }}) + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ steps.vars.outputs.RUST_MIN_SRV }} + default: true + profile: minimal # minimal component installation (ie, no documentation) + - name: Install `cargo-tree` # for dependency information + uses: actions-rs/install@v0.1 + with: + crate: cargo-tree + version: latest + use-tool-cache: true + env: + RUSTUP_TOOLCHAIN: stable + - name: Ensure updated 'Cargo.lock' + shell: bash + run: | + # Ensure updated 'Cargo.lock' + # * 'Cargo.lock' is required to be in a format that `cargo` of MinSRV can interpret (eg, v1-format for MinSRV < v1.38) + cargo fetch --locked --quiet || cargo +${{ steps.vars.outputs.RUST_MIN_SRV }} update + - name: Info + shell: bash + run: | + # Info + ## environment + echo "## environment" + echo "CI='${CI}'" + ## tooling info display + echo "## tooling" + which gcc >/dev/null 2>&1 && (gcc --version | head -1) || true + rustup -V + rustup show active-toolchain + cargo -V + rustc -V + cargo-tree tree -V + ## dependencies + echo "## dependency list" + cargo fetch --locked --quiet + ## * using the 'stable' toolchain is necessary to avoid "unexpected '--filter-platform'" errors + RUSTUP_TOOLCHAIN=stable cargo-tree tree --frozen --all --no-dev-dependencies --no-indent --features ${{ matrix.job.features }} | grep -vE "$PWD" | sort --unique + - name: Commit any changes (to '${{ env.BRANCH_TARGET }}') + uses: EndBug/add-and-commit@v7 + with: + branch: ${{ env.BRANCH_TARGET }} + default_author: github_actions + message: "maint ~ refresh 'Cargo.lock'" + add: Cargo.lock + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + code_format: + # Recheck/refresh code formatting + if: github.event.pull_request.merged == true ## only for PR merges + name: Update/format + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + job: + - { os: ubuntu-latest , features: feat_os_unix } + steps: + - uses: actions/checkout@v2 + - name: Initialize job variables + id: vars + shell: bash + run: | + ## VARs setup + outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } + # target-specific options + # * CARGO_FEATURES_OPTION + CARGO_FEATURES_OPTION='' ; + if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi + outputs CARGO_FEATURES_OPTION + - name: Install `rust` toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + default: true + profile: minimal # minimal component installation (ie, no documentation) + components: rustfmt + - name: "`cargo fmt`" + shell: bash + run: | + cargo fmt + - name: "`cargo fmt` tests" + shell: bash + run: | + # `cargo fmt` of tests + find tests -name "*.rs" -print0 | xargs -0 cargo fmt -- + - name: Commit any changes (to '${{ env.BRANCH_TARGET }}') + uses: EndBug/add-and-commit@v7 + with: + branch: ${{ env.BRANCH_TARGET }} + default_author: github_actions + message: "maint ~ rustfmt (`cargo fmt`)" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From b4a06cfdbad6db079f4a836996d06aef0c068986 Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Sat, 12 Jun 2021 23:27:03 -0500 Subject: [PATCH 23/37] maint/CICD ~ refactor; improve logging for `outputs` shell script --- .github/workflows/CICD.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 9d2edeac1..a8046269a 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -32,7 +32,7 @@ jobs: shell: bash run: | ## VARs setup - outputs() { for var in "$@" ; do echo steps.vars.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } + outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } # target-specific options # * CARGO_FEATURES_OPTION CARGO_FEATURES_OPTION='' ; @@ -66,7 +66,7 @@ jobs: shell: bash run: | ## VARs setup - outputs() { for var in "$@" ; do echo steps.vars.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } + outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } # target-specific options # * CARGO_FEATURES_OPTION CARGO_FEATURES_OPTION='' ; @@ -110,7 +110,7 @@ jobs: shell: bash run: | ## VARs setup - outputs() { for var in "$@" ; do echo steps.vars.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } + outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } # target-specific options # * CARGO_FEATURES_OPTION CARGO_FEATURES_OPTION='' ; @@ -276,7 +276,7 @@ jobs: shell: bash run: | ## VARs setup - outputs() { for var in "$@" ; do echo steps.vars.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } + outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } # toolchain TOOLCHAIN="stable" ## default to "stable" toolchain # * specify alternate/non-default TOOLCHAIN for *-pc-windows-gnu targets; gnu targets on Windows are broken for the standard *-pc-windows-msvc toolchain (refs: GH:rust-lang/rust#47048, GH:rust-lang/rust#53454, GH:rust-lang/cargo#6754) @@ -382,7 +382,7 @@ jobs: shell: bash run: | ## Dependent VARs setup - outputs() { for var in "$@" ; do echo steps.vars.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } + outputs() { step_id="dep_vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } # * determine sub-crate utility list UTILITY_LIST="$(./util/show-utils.sh ${CARGO_FEATURES_OPTION})" echo UTILITY_LIST=${UTILITY_LIST} @@ -544,7 +544,7 @@ jobs: shell: bash run: | ## VARs setup - outputs() { for var in "$@" ; do echo steps.vars.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } + outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } # toolchain TOOLCHAIN="nightly-${{ env.RUST_COV_SRV }}" ## default to "nightly" toolchain (required for certain required unstable compiler flags) ## !maint: refactor when stable channel has needed support # * specify gnu-type TOOLCHAIN for windows; `grcov` requires gnu-style code coverage data files @@ -579,7 +579,7 @@ jobs: shell: bash run: | ## Dependent VARs setup - outputs() { for var in "$@" ; do echo steps.vars.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } + outputs() { step_id="dep_vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } # * determine sub-crate utility list UTILITY_LIST="$(./util/show-utils.sh ${CARGO_FEATURES_OPTION})" CARGO_UTILITY_LIST_OPTIONS="$(for u in ${UTILITY_LIST}; do echo "-puu_${u}"; done;)" From c74bc2eedd10b1575836056e72a322184ec6f7d9 Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Sun, 13 Jun 2021 00:36:05 -0500 Subject: [PATCH 24/37] maint/CICD ~ add 'GHA-delete-GNU-workflow-logs' shell script utility --- util/GHA-delete-GNU-workflow-logs.sh | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 util/GHA-delete-GNU-workflow-logs.sh diff --git a/util/GHA-delete-GNU-workflow-logs.sh b/util/GHA-delete-GNU-workflow-logs.sh new file mode 100644 index 000000000..19e3311d4 --- /dev/null +++ b/util/GHA-delete-GNU-workflow-logs.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +# spell-checker:ignore (utils) gitsome jq ; (gh) repos + +ME="${0}" +ME_dir="$(dirname -- "${ME}")" +ME_parent_dir="$(dirname -- "${ME_dir}")" +ME_parent_dir_abs="$(realpath -mP -- "${ME_parent_dir}")" + +# ref: + +# note: requires `gh` and `jq` + +## tools available? + +# * `gh` available? +unset GH +gh --version 1>/dev/null 2>&1 +if [ $? -eq 0 ]; then export GH="gh"; fi + +# * `jq` available? +unset JQ +jq --version 1>/dev/null 2>&1 +if [ $? -eq 0 ]; then export JQ="jq"; fi + +if [ -z "${GH}" ] || [ -z "${JQ}" ]; then + if [ -z "${GH}" ]; then + echo 'ERR!: missing `gh` (see install instructions at )' 1>&2 + fi + if [ -z "${JQ}" ]; then + echo 'ERR!: missing `jq` (install with `sudo apt install jq`)' 1>&2 + fi + exit 1 +fi + +dry_run=true + +USER_NAME=uutils +REPO_NAME=coreutils +WORK_NAME=GNU + +# * `--paginate` retrieves all pages +# gh api --paginate "repos/${USER_NAME}/${REPO_NAME}/actions/runs" | jq -r ".workflow_runs[] | select(.name == \"${WORK_NAME}\") | (.id)" | xargs -n1 sh -c "for arg do { echo gh api repos/${USER_NAME}/${REPO_NAME}/actions/runs/\${arg} -X DELETE ; if [ -z "$dry_run" ]; then gh api repos/${USER_NAME}/${REPO_NAME}/actions/runs/\${arg} -X DELETE ; fi ; } ; done ;" _ +gh api "repos/${USER_NAME}/${REPO_NAME}/actions/runs" | jq -r ".workflow_runs[] | select(.name == \"${WORK_NAME}\") | (.id)" | xargs -n1 sh -c "for arg do { echo gh api repos/${USER_NAME}/${REPO_NAME}/actions/runs/\${arg} -X DELETE ; if [ -z "$dry_run" ]; then gh api repos/${USER_NAME}/${REPO_NAME}/actions/runs/\${arg} -X DELETE ; fi ; } ; done ;" _ From f5edc500e03dc3fc37339e1b5441a4224d460b34 Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Sat, 19 Jun 2021 10:53:06 -0500 Subject: [PATCH 25/37] tests ~ fix spelling errors --- tests/by-util/test_cut.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/by-util/test_cut.rs b/tests/by-util/test_cut.rs index e21010ec8..92bab4d75 100644 --- a/tests/by-util/test_cut.rs +++ b/tests/by-util/test_cut.rs @@ -162,7 +162,7 @@ fn test_directory_and_no_such_file() { fn test_equal_as_delimiter() { new_ucmd!() .args(&["-f", "2", "-d="]) - .pipe_in("--libdir=./out/lib") + .pipe_in("--dir=./out/lib") .succeeds() .stdout_only("./out/lib\n"); } From f6cb1324b630b8b0208efadc789d117557a81189 Mon Sep 17 00:00:00 2001 From: Anup Mahindre Date: Sun, 20 Jun 2021 13:19:50 +0530 Subject: [PATCH 26/37] ls: Fix problems dealing with dangling symlinks - For dangling symlinks, errors should only be reported if dereferencing options were passed and dereferencing was applicable to the particular symlink - With -i parameter, report '?' as the inode number for dangling symlinks --- src/uu/ls/src/ls.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 0bffa2e52..677556ab0 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -1196,7 +1196,9 @@ fn list(locs: Vec, config: Config) -> i32 { for loc in &locs { let p = PathBuf::from(&loc); - if !p.exists() { + let path_data = PathData::new(p, None, None, &config, true); + + if !path_data.md().is_some() { show_error!("'{}': {}", &loc, "No such file or directory"); /* We found an error, the return code of ls should not be 0 @@ -1206,8 +1208,6 @@ fn list(locs: Vec, config: Config) -> i32 { continue; } - let path_data = PathData::new(p, None, None, &config, true); - let show_dir_contents = match path_data.file_type() { Some(ft) => !config.directory && ft.is_dir(), None => { @@ -1331,7 +1331,7 @@ fn enter_directory(dir: &PathData, config: &Config, out: &mut BufWriter) fn get_metadata(entry: &Path, dereference: bool) -> std::io::Result { if dereference { - entry.metadata().or_else(|_| entry.symlink_metadata()) + entry.metadata() } else { entry.symlink_metadata() } @@ -1733,7 +1733,11 @@ fn display_file_name(path: &PathData, config: &Config) -> Option { #[cfg(unix)] { if config.format != Format::Long && config.inode { - name = get_inode(path.md()?) + " " + &name; + name = path + .md() + .map_or_else(|| "?".to_string(), |md| get_inode(md)) + + " " + + &name; } } From d0039df8c3de451a07a52dc96f1d6b4d71181817 Mon Sep 17 00:00:00 2001 From: Anup Mahindre Date: Sun, 20 Jun 2021 13:50:38 +0530 Subject: [PATCH 27/37] tests: Add test for dangling symlinks with ls Add test similar to gnu dangling symlinks test --- tests/by-util/test_ls.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index f8aa4453b..741a304e3 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -2021,3 +2021,28 @@ fn test_ls_path() { .run() .stdout_is(expected_stdout); } + +#[test] +fn test_ls_dangling_symlinks() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + at.mkdir("temp_dir"); + at.symlink_file("does_not_exist", "temp_dir/dangle"); + + scene.ucmd().arg("-L").arg("temp_dir/dangle").fails(); + scene.ucmd().arg("-H").arg("temp_dir/dangle").fails(); + + scene + .ucmd() + .arg("temp_dir/dangle") + .succeeds() + .stdout_contains("dangle"); + + scene + .ucmd() + .arg("-Li") + .arg("temp_dir") + .succeeds() // this should fail, though at the moment, ls lacks a way to propagate errors encountered during display + .stdout_contains("? dangle"); +} From 3b641afadcf57facf160b57c5e90bfeeeb68bed7 Mon Sep 17 00:00:00 2001 From: Anup Mahindre Date: Sun, 20 Jun 2021 16:56:25 +0530 Subject: [PATCH 28/37] ls: Fix issue with Windows and dangling symbolic links - Windows hidden file attribute determination would assume symbolic link to be valid and would panic - Check symbolic link's attributes if the link points to non-existing file --- src/uu/ls/src/ls.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 677556ab0..220eccb30 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -1270,7 +1270,8 @@ fn sort_entries(entries: &mut Vec, config: &Config) { #[cfg(windows)] fn is_hidden(file_path: &DirEntry) -> bool { - let metadata = fs::metadata(file_path.path()).unwrap(); + let path = file_path.path(); + let metadata = fs::metadata(&path).unwrap_or_else(|_| fs::symlink_metadata(&path).unwrap()); let attr = metadata.file_attributes(); (attr & 0x2) > 0 } From ffb6b7152f8699b0a42fe811a576a6a3633f4baf Mon Sep 17 00:00:00 2001 From: Anup Mahindre Date: Sun, 20 Jun 2021 16:58:28 +0530 Subject: [PATCH 29/37] tests: Fix ls dangling symbolic links test output for windows On windows we do not print inode numbers at all, so skip checking for ? for dangling symbolic links in expected output --- tests/by-util/test_ls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 741a304e3..67112b4f5 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -2044,5 +2044,5 @@ fn test_ls_dangling_symlinks() { .arg("-Li") .arg("temp_dir") .succeeds() // this should fail, though at the moment, ls lacks a way to propagate errors encountered during display - .stdout_contains("? dangle"); + .stdout_contains(if cfg!(windows) { "dangle" } else { "? dangle" }); } From a91369bbff9f6587bfd23d76f043be25bfdbd294 Mon Sep 17 00:00:00 2001 From: Michael Debertol Date: Sun, 20 Jun 2021 19:10:51 +0200 Subject: [PATCH 30/37] cp: fix dead code warnings on windows --- src/uu/cp/src/cp.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 7cf6a1d9b..9186ec259 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1242,6 +1242,7 @@ fn copy_helper(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> return Err("--reflink is only supported on linux and macOS" .to_string() .into()); + #[cfg(any(target_os = "linux", target_os = "macos"))] if is_symlink { assert!(options.dereference); let real_path = std::fs::read_link(source)?; From 6aa79440f5c9ff2015a2b2d3c184b2c82699a4c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Lauzier?= Date: Sun, 20 Jun 2021 21:21:50 -0400 Subject: [PATCH 31/37] Fix a clippy warning --- src/uu/timeout/src/timeout.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/timeout/src/timeout.rs b/src/uu/timeout/src/timeout.rs index bc92157ca..f21a0265f 100644 --- a/src/uu/timeout/src/timeout.rs +++ b/src/uu/timeout/src/timeout.rs @@ -89,8 +89,8 @@ impl Config { signal, duration, preserve_status, - command, verbose, + command, } } } From 4b3224dd82d2f80bdca5598675f397c5577a568d Mon Sep 17 00:00:00 2001 From: Anup Mahindre Date: Mon, 21 Jun 2021 20:29:22 +0530 Subject: [PATCH 32/37] ls: Fix clippy warning --- src/uu/ls/src/ls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 220eccb30..1d050a376 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -1198,7 +1198,7 @@ fn list(locs: Vec, config: Config) -> i32 { let p = PathBuf::from(&loc); let path_data = PathData::new(p, None, None, &config, true); - if !path_data.md().is_some() { + if path_data.md().is_none() { show_error!("'{}': {}", &loc, "No such file or directory"); /* We found an error, the return code of ls should not be 0 From e5a7bcbb9d6a29d0f9c3b8911ed4d4764cf51882 Mon Sep 17 00:00:00 2001 From: Michael Debertol Date: Mon, 21 Jun 2021 21:13:40 +0200 Subject: [PATCH 33/37] tests: keep env vars for the temporary directory On some Windows machines this would otherwise cause `std::env::temp_dir` to fall back to a path that is not writeable (C:\\Windows). Since by default integration tests don't inherit env vars from the parent, we have to override this in some cases. --- tests/by-util/test_mktemp.rs | 4 ++-- tests/by-util/test_sort.rs | 15 ++++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/tests/by-util/test_mktemp.rs b/tests/by-util/test_mktemp.rs index d601bad5b..413b35bc5 100644 --- a/tests/by-util/test_mktemp.rs +++ b/tests/by-util/test_mktemp.rs @@ -386,7 +386,7 @@ fn test_mktemp_tmpdir_one_arg() { let scene = TestScenario::new(util_name!()); let result = scene - .ucmd() + .ucmd_keepenv() .arg("--tmpdir") .arg("apt-key-gpghome.XXXXXXXXXX") .succeeds(); @@ -399,7 +399,7 @@ fn test_mktemp_directory_tmpdir() { let scene = TestScenario::new(util_name!()); let result = scene - .ucmd() + .ucmd_keepenv() .arg("--directory") .arg("--tmpdir") .arg("apt-key-gpghome.XXXXXXXXXX") diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index 0f9a9d3f1..01fafae00 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -28,7 +28,8 @@ fn test_helper(file_name: &str, possible_args: &[&str]) { fn test_buffer_sizes() { let buffer_sizes = ["0", "50K", "50k", "1M", "100M"]; for buffer_size in &buffer_sizes { - new_ucmd!() + TestScenario::new(util_name!()) + .ucmd_keepenv() .arg("-n") .arg("-S") .arg(buffer_size) @@ -40,7 +41,8 @@ fn test_buffer_sizes() { { let buffer_sizes = ["1000G", "10T"]; for buffer_size in &buffer_sizes { - new_ucmd!() + TestScenario::new(util_name!()) + .ucmd_keepenv() .arg("-n") .arg("-S") .arg(buffer_size) @@ -877,7 +879,8 @@ fn test_compress() { #[test] fn test_compress_fail() { - new_ucmd!() + TestScenario::new(util_name!()) + .ucmd_keepenv() .args(&[ "ext_sort.txt", "-n", @@ -892,7 +895,8 @@ fn test_compress_fail() { #[test] fn test_merge_batches() { - new_ucmd!() + TestScenario::new(util_name!()) + .ucmd_keepenv() .args(&["ext_sort.txt", "-n", "-S", "150b"]) .succeeds() .stdout_only_fixture("ext_sort.expected"); @@ -900,7 +904,8 @@ fn test_merge_batches() { #[test] fn test_merge_batch_size() { - new_ucmd!() + TestScenario::new(util_name!()) + .ucmd_keepenv() .arg("--batch-size=2") .arg("-m") .arg("--unique") From 622504467f369198ce5839560617130199b6b917 Mon Sep 17 00:00:00 2001 From: Michael Debertol Date: Tue, 22 Jun 2021 17:36:56 +0200 Subject: [PATCH 34/37] mktemp: note that windows uses a different env var for tmpdir On windows `std::env::temp_dir` uses the `TMP` environment variable instead of `TMPDIR`. --- src/uu/mktemp/src/mktemp.rs | 4 ++-- tests/by-util/test_mktemp.rs | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/uu/mktemp/src/mktemp.rs b/src/uu/mktemp/src/mktemp.rs index e04de8702..bd77e9d51 100644 --- a/src/uu/mktemp/src/mktemp.rs +++ b/src/uu/mktemp/src/mktemp.rs @@ -77,14 +77,14 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .long(OPT_TMPDIR) .help( "interpret TEMPLATE relative to DIR; if DIR is not specified, use \ - $TMPDIR if set, else /tmp. With this option, TEMPLATE must not \ + $TMPDIR ($TMP on windows) if set, else /tmp. With this option, TEMPLATE must not \ be an absolute name; unlike with -t, TEMPLATE may contain \ slashes, but mktemp creates only the final component", ) .value_name("DIR"), ) .arg(Arg::with_name(OPT_T).short(OPT_T).help( - "Generate a template (using the supplied prefix and TMPDIR if set) \ + "Generate a template (using the supplied prefix and TMPDIR (TMP on windows) if set) \ to create a filename template [deprecated]", )) .arg( diff --git a/tests/by-util/test_mktemp.rs b/tests/by-util/test_mktemp.rs index 413b35bc5..bcf75ee20 100644 --- a/tests/by-util/test_mktemp.rs +++ b/tests/by-util/test_mktemp.rs @@ -17,7 +17,10 @@ static TEST_TEMPLATE8: &str = "tempXXXl/ate"; #[cfg(windows)] static TEST_TEMPLATE8: &str = "tempXXXl\\ate"; +#[cfg(not(windows))] const TMPDIR: &str = "TMPDIR"; +#[cfg(windows)] +const TMPDIR: &str = "TMP"; #[test] fn test_mktemp_mktemp() { From ce0801db909c979ad2cce37d25879159d71dbbc2 Mon Sep 17 00:00:00 2001 From: Michael Debertol Date: Tue, 22 Jun 2021 15:31:49 +0200 Subject: [PATCH 35/37] tests/mv: test uutils mv instead of system util Calling `cmd_keepenv("mv")` spawned the system `mv` instead of the uutils `mv`. Also, `keepenv` isn't necessary because it doesn't need to inherit environment variables. We now actually check the stderr, because previously the result of `ends_with` was not used, making the test pass even when it shouldn't. I disabled the test on windows because `mkdir` does not support `-m` on windows, making the test fail because there will be no permission error. On FreeBSD there isn't a permission error either, and `mv` succeeds. --- tests/by-util/test_mv.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/by-util/test_mv.rs b/tests/by-util/test_mv.rs index 2f35bf5eb..d8733a4f0 100644 --- a/tests/by-util/test_mv.rs +++ b/tests/by-util/test_mv.rs @@ -729,6 +729,7 @@ fn test_mv_verbose() { } #[test] +#[cfg(target_os = "linux")] // mkdir does not support -m on windows. Freebsd doesn't return a permission error either. fn test_mv_permission_error() { let scene = TestScenario::new("mkdir"); let folder1 = "bar"; @@ -738,12 +739,11 @@ fn test_mv_permission_error() { scene.ucmd().arg("-m777").arg(folder2).succeeds(); scene - .cmd_keepenv(util_name!()) + .ccmd("mv") .arg(folder2) .arg(folder_to_move) - .run() - .stderr_str() - .ends_with("Permission denied"); + .fails() + .stderr_contains("Permission denied"); } // Todo: From d60afb89472c0166d4c884239d8b5784f518d1b4 Mon Sep 17 00:00:00 2001 From: Michael Debertol Date: Tue, 22 Jun 2021 16:02:50 +0200 Subject: [PATCH 36/37] mkdir: note that -m is not supported on windows --- src/uu/mkdir/src/mkdir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/mkdir/src/mkdir.rs b/src/uu/mkdir/src/mkdir.rs index e8a8ef2db..c5ff8b76c 100644 --- a/src/uu/mkdir/src/mkdir.rs +++ b/src/uu/mkdir/src/mkdir.rs @@ -40,7 +40,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { Arg::with_name(OPT_MODE) .short("m") .long(OPT_MODE) - .help("set file mode") + .help("set file mode (not implemented on windows)") .default_value("755"), ) .arg( From c0be9796112439b8e26315572a4f7cfa4964b079 Mon Sep 17 00:00:00 2001 From: Jan Scheer Date: Tue, 22 Jun 2021 00:22:30 +0200 Subject: [PATCH 37/37] fix some issues with locale (replace "LANGUAGE" with "LC_ALL") MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `LANGUAGE=C` is not enough, `LC_ALL=C` is needed as the environment variable that overrides all the other localization settings. e.g. ```bash $ LANGUAGE=C id foobar id: ‘foobar’: no such user $ LC_ALL=C id foobar id: 'foobar': no such user ``` * replace `LANGUAGE` with `LC_ALL` as environment variable in the tests * fix the the date string of affected uutils * replace `‘` and `’` with `'` --- src/uu/base32/src/base_common.rs | 4 +-- src/uu/chown/src/chown.rs | 4 +-- src/uu/cp/src/cp.rs | 2 +- src/uu/date/src/date.rs | 6 ++-- src/uu/dircolors/src/dircolors.rs | 4 +-- src/uu/du/src/du.rs | 14 ++++----- src/uu/id/src/id.rs | 2 +- src/uu/ls/src/ls.rs | 4 +-- src/uu/mknod/src/mknod.rs | 2 +- src/uu/mktemp/src/mktemp.rs | 2 +- src/uu/mv/src/mv.rs | 22 +++++++------- src/uu/numfmt/src/format.rs | 6 ++-- src/uu/numfmt/src/numfmt.rs | 2 +- src/uu/pinky/src/pinky.rs | 2 +- src/uu/split/src/split.rs | 2 +- src/uu/stat/src/stat.rs | 2 +- src/uu/test/src/parser.rs | 4 +-- src/uu/test/src/test.rs | 8 +++--- src/uu/tr/src/tr.rs | 2 +- src/uu/truncate/src/truncate.rs | 2 +- src/uu/who/src/who.rs | 6 ++-- src/uucore/src/lib/parser/parse_size.rs | 38 ++++++++++++------------- tests/by-util/test_base32.rs | 4 +-- tests/by-util/test_base64.rs | 4 +-- tests/by-util/test_chown.rs | 4 +-- tests/by-util/test_date.rs | 2 +- tests/by-util/test_du.rs | 2 +- tests/by-util/test_head.rs | 10 +++---- tests/by-util/test_id.rs | 4 +-- tests/by-util/test_ls.rs | 2 +- tests/by-util/test_mv.rs | 18 ++++++------ tests/by-util/test_numfmt.rs | 20 ++++++------- tests/by-util/test_pinky.rs | 2 +- tests/by-util/test_split.rs | 8 +++--- tests/by-util/test_stat.rs | 2 +- tests/by-util/test_stdbuf.rs | 4 +-- tests/by-util/test_tail.rs | 10 +++---- tests/by-util/test_test.rs | 10 +++---- tests/by-util/test_truncate.rs | 14 ++++----- tests/by-util/test_users.rs | 2 +- tests/by-util/test_who.rs | 13 ++++----- 41 files changed, 135 insertions(+), 140 deletions(-) diff --git a/src/uu/base32/src/base_common.rs b/src/uu/base32/src/base_common.rs index 256b674e2..a606351ce 100644 --- a/src/uu/base32/src/base_common.rs +++ b/src/uu/base32/src/base_common.rs @@ -39,7 +39,7 @@ impl Config { Some(mut values) => { let name = values.next().unwrap(); if values.len() != 0 { - return Err(format!("extra operand ‘{}’", name)); + return Err(format!("extra operand '{}'", name)); } if name == "-" { @@ -58,7 +58,7 @@ impl Config { .value_of(options::WRAP) .map(|num| { num.parse::() - .map_err(|e| format!("Invalid wrap size: ‘{}’: {}", num, e)) + .map_err(|e| format!("Invalid wrap size: '{}': {}", num, e)) }) .transpose()?; diff --git a/src/uu/chown/src/chown.rs b/src/uu/chown/src/chown.rs index ab9f10dba..7fc7f04d3 100644 --- a/src/uu/chown/src/chown.rs +++ b/src/uu/chown/src/chown.rs @@ -281,7 +281,7 @@ fn parse_spec(spec: &str) -> Result<(Option, Option), String> { let uid = if usr_only || usr_grp { Some( Passwd::locate(args[0]) - .map_err(|_| format!("invalid user: ‘{}’", spec))? + .map_err(|_| format!("invalid user: '{}'", spec))? .uid(), ) } else { @@ -290,7 +290,7 @@ fn parse_spec(spec: &str) -> Result<(Option, Option), String> { let gid = if grp_only || usr_grp { Some( Group::locate(args[1]) - .map_err(|_| format!("invalid group: ‘{}’", spec))? + .map_err(|_| format!("invalid group: '{}'", spec))? .gid(), ) } else { diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 851117bde..0d7946b06 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1254,7 +1254,7 @@ fn copy_link(source: &Path, dest: &Path) -> CopyResult<()> { Some(name) => dest.join(name).into(), None => crash!( EXIT_ERR, - "cannot stat ‘{}’: No such file or directory", + "cannot stat '{}': No such file or directory", source.display() ), } diff --git a/src/uu/date/src/date.rs b/src/uu/date/src/date.rs index 8a0e3ef3a..11c3eb31f 100644 --- a/src/uu/date/src/date.rs +++ b/src/uu/date/src/date.rs @@ -210,7 +210,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let format = if let Some(form) = matches.value_of(OPT_FORMAT) { if !form.starts_with('+') { - eprintln!("date: invalid date ‘{}’", form); + eprintln!("date: invalid date '{}'", form); return 1; } let form = form[1..].to_string(); @@ -239,7 +239,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let set_to = match matches.value_of(OPT_SET).map(parse_date) { None => None, Some(Err((input, _err))) => { - eprintln!("date: invalid date ‘{}’", input); + eprintln!("date: invalid date '{}'", input); return 1; } Some(Ok(date)) => Some(date), @@ -305,7 +305,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { println!("{}", formatted); } Err((input, _err)) => { - println!("date: invalid date ‘{}’", input); + println!("date: invalid date '{}'", input); } } } diff --git a/src/uu/dircolors/src/dircolors.rs b/src/uu/dircolors/src/dircolors.rs index 2fa2e8b91..8a01d77d6 100644 --- a/src/uu/dircolors/src/dircolors.rs +++ b/src/uu/dircolors/src/dircolors.rs @@ -123,7 +123,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { if matches.is_present(options::PRINT_DATABASE) { if !files.is_empty() { show_usage_error!( - "extra operand ‘{}’\nfile operands cannot be combined with \ + "extra operand '{}'\nfile operands cannot be combined with \ --print-database (-p)", files[0] ); @@ -155,7 +155,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { result = parse(INTERNAL_DB.lines(), out_format, "") } else { if files.len() > 1 { - show_usage_error!("extra operand ‘{}’", files[1]); + show_usage_error!("extra operand '{}'", files[1]); return 1; } match File::open(files[0]) { diff --git a/src/uu/du/src/du.rs b/src/uu/du/src/du.rs index fa6c34165..623faf62c 100644 --- a/src/uu/du/src/du.rs +++ b/src/uu/du/src/du.rs @@ -274,7 +274,7 @@ fn du( Err(e) => { safe_writeln!( stderr(), - "{}: cannot read directory ‘{}‘: {}", + "{}: cannot read directory '{}': {}", options.program_name, my_stat.path.display(), e @@ -318,9 +318,7 @@ fn du( let error_message = "Permission denied"; show_error_custom_description!(description, "{}", error_message) } - _ => { - show_error!("cannot access '{}': {}", entry.path().display(), error) - } + _ => show_error!("cannot access '{}': {}", entry.path().display(), error), }, }, Err(error) => show_error!("{}", error), @@ -594,9 +592,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let files = match matches.value_of(options::FILE) { Some(_) => matches.values_of(options::FILE).unwrap().collect(), - None => { - vec!["."] - } + None => vec!["."], }; let block_size = u64::try_from(read_block_size(matches.value_of(options::BLOCK_SIZE))).unwrap(); @@ -693,8 +689,8 @@ Try '{} --help' for more information.", time } else { show_error!( - "Invalid argument ‘{}‘ for --time. -‘birth‘ and ‘creation‘ arguments are not supported on this platform.", + "Invalid argument '{}' for --time. +'birth' and 'creation' arguments are not supported on this platform.", s ); return 1; diff --git a/src/uu/id/src/id.rs b/src/uu/id/src/id.rs index 9037745eb..176240f0c 100644 --- a/src/uu/id/src/id.rs +++ b/src/uu/id/src/id.rs @@ -269,7 +269,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { match Passwd::locate(users[i].as_str()) { Ok(p) => Some(p), Err(_) => { - show_error!("‘{}’: no such user", users[i]); + show_error!("'{}': no such user", users[i]); exit_code = 1; if i + 1 >= users.len() { break; diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 0bffa2e52..e01bba8dc 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -373,7 +373,7 @@ impl Config { .value_of(options::WIDTH) .map(|x| { x.parse::().unwrap_or_else(|_e| { - show_error!("invalid line width: ‘{}’", x); + show_error!("invalid line width: '{}'", x); exit(2); }) }) @@ -756,7 +756,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { Arg::with_name(options::time::CHANGE) .short(options::time::CHANGE) .help("If the long listing format (e.g., -l, -o) is being used, print the status \ - change time (the ‘ctime’ in the inode) instead of the modification time. When \ + change time (the 'ctime' in the inode) instead of the modification time. When \ explicitly sorting by time (--sort=time or -t) or when not using a long listing \ format, sort according to the status change time.") .overrides_with_all(&[ diff --git a/src/uu/mknod/src/mknod.rs b/src/uu/mknod/src/mknod.rs index e5e6ef1fa..a1f361e55 100644 --- a/src/uu/mknod/src/mknod.rs +++ b/src/uu/mknod/src/mknod.rs @@ -210,7 +210,7 @@ fn valid_type(tpe: String) -> Result<(), String> { if vec!['b', 'c', 'u', 'p'].contains(&first_char) { Ok(()) } else { - Err(format!("invalid device type ‘{}’", tpe)) + Err(format!("invalid device type '{}'", tpe)) } }) } diff --git a/src/uu/mktemp/src/mktemp.rs b/src/uu/mktemp/src/mktemp.rs index e04de8702..b0bc3474b 100644 --- a/src/uu/mktemp/src/mktemp.rs +++ b/src/uu/mktemp/src/mktemp.rs @@ -154,7 +154,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { if matches.is_present(OPT_TMPDIR) && PathBuf::from(prefix).is_absolute() { show_error!( - "invalid template, ‘{}’; with --tmpdir, it may not be absolute", + "invalid template, '{}'; with --tmpdir, it may not be absolute", template ); return 1; diff --git a/src/uu/mv/src/mv.rs b/src/uu/mv/src/mv.rs index bb402737e..d709a2117 100644 --- a/src/uu/mv/src/mv.rs +++ b/src/uu/mv/src/mv.rs @@ -230,7 +230,7 @@ fn exec(files: &[PathBuf], b: Behavior) -> i32 { // lacks permission to access metadata. if source.symlink_metadata().is_err() { show_error!( - "cannot stat ‘{}’: No such file or directory", + "cannot stat '{}': No such file or directory", source.display() ); return 1; @@ -240,7 +240,7 @@ fn exec(files: &[PathBuf], b: Behavior) -> i32 { if b.no_target_dir { if !source.is_dir() { show_error!( - "cannot overwrite directory ‘{}’ with non-directory", + "cannot overwrite directory '{}' with non-directory", target.display() ); return 1; @@ -249,7 +249,7 @@ fn exec(files: &[PathBuf], b: Behavior) -> i32 { return match rename(source, target, &b) { Err(e) => { show_error!( - "cannot move ‘{}’ to ‘{}’: {}", + "cannot move '{}' to '{}': {}", source.display(), target.display(), e.to_string() @@ -263,7 +263,7 @@ fn exec(files: &[PathBuf], b: Behavior) -> i32 { return move_files_into_dir(&[source.clone()], target, &b); } else if target.exists() && source.is_dir() { show_error!( - "cannot overwrite non-directory ‘{}’ with directory ‘{}’", + "cannot overwrite non-directory '{}' with directory '{}'", target.display(), source.display() ); @@ -278,7 +278,7 @@ fn exec(files: &[PathBuf], b: Behavior) -> i32 { _ => { if b.no_target_dir { show_error!( - "mv: extra operand ‘{}’\n\ + "mv: extra operand '{}'\n\ Try '{} --help' for more information.", files[2].display(), executable!() @@ -294,7 +294,7 @@ fn exec(files: &[PathBuf], b: Behavior) -> i32 { fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> i32 { if !target_dir.is_dir() { - show_error!("target ‘{}’ is not a directory", target_dir.display()); + show_error!("target '{}' is not a directory", target_dir.display()); return 1; } @@ -304,7 +304,7 @@ fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> i3 Some(name) => target_dir.join(name), None => { show_error!( - "cannot stat ‘{}’: No such file or directory", + "cannot stat '{}': No such file or directory", sourcepath.display() ); @@ -315,7 +315,7 @@ fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> i3 if let Err(e) = rename(sourcepath, &targetpath, b) { show_error!( - "cannot move ‘{}’ to ‘{}’: {}", + "cannot move '{}' to '{}': {}", sourcepath.display(), targetpath.display(), e.to_string() @@ -338,7 +338,7 @@ fn rename(from: &Path, to: &Path, b: &Behavior) -> io::Result<()> { match b.overwrite { OverwriteMode::NoClobber => return Ok(()), OverwriteMode::Interactive => { - println!("{}: overwrite ‘{}’? ", executable!(), to.display()); + println!("{}: overwrite '{}'? ", executable!(), to.display()); if !read_yes() { return Ok(()); } @@ -371,9 +371,9 @@ fn rename(from: &Path, to: &Path, b: &Behavior) -> io::Result<()> { rename_with_fallback(from, to)?; if b.verbose { - print!("‘{}’ -> ‘{}’", from.display(), to.display()); + print!("'{}' -> '{}'", from.display(), to.display()); match backup_path { - Some(path) => println!(" (backup: ‘{}’)", path.display()), + Some(path) => println!(" (backup: '{}')", path.display()), None => println!(), } } diff --git a/src/uu/numfmt/src/format.rs b/src/uu/numfmt/src/format.rs index ee692d8f0..54e122215 100644 --- a/src/uu/numfmt/src/format.rs +++ b/src/uu/numfmt/src/format.rs @@ -62,7 +62,7 @@ impl<'a> Iterator for WhitespaceSplitter<'a> { fn parse_suffix(s: &str) -> Result<(f64, Option)> { if s.is_empty() { - return Err("invalid number: ‘’".to_string()); + return Err("invalid number: ''".to_string()); } let with_i = s.ends_with('i'); @@ -80,7 +80,7 @@ fn parse_suffix(s: &str) -> Result<(f64, Option)> { Some('Z') => Ok(Some((RawSuffix::Z, with_i))), Some('Y') => Ok(Some((RawSuffix::Y, with_i))), Some('0'..='9') => Ok(None), - _ => Err(format!("invalid suffix in input: ‘{}’", s)), + _ => Err(format!("invalid suffix in input: '{}'", s)), }?; let suffix_len = match suffix { @@ -91,7 +91,7 @@ fn parse_suffix(s: &str) -> Result<(f64, Option)> { let number = s[..s.len() - suffix_len] .parse::() - .map_err(|_| format!("invalid number: ‘{}’", s))?; + .map_err(|_| format!("invalid number: '{}'", s))?; Ok((number, suffix)) } diff --git a/src/uu/numfmt/src/numfmt.rs b/src/uu/numfmt/src/numfmt.rs index 086336437..88cb008cc 100644 --- a/src/uu/numfmt/src/numfmt.rs +++ b/src/uu/numfmt/src/numfmt.rs @@ -114,7 +114,7 @@ fn parse_options(args: &ArgMatches) -> Result { 0 => Err(value), _ => Ok(n), }) - .map_err(|value| format!("invalid header value ‘{}’", value)) + .map_err(|value| format!("invalid header value '{}'", value)) } }?; diff --git a/src/uu/pinky/src/pinky.rs b/src/uu/pinky/src/pinky.rs index d15730b32..33dcff274 100644 --- a/src/uu/pinky/src/pinky.rs +++ b/src/uu/pinky/src/pinky.rs @@ -234,7 +234,7 @@ fn idle_string(when: i64) -> String { } fn time_string(ut: &Utmpx) -> String { - time::strftime("%Y-%m-%d %H:%M", &ut.login_time()).unwrap() + time::strftime("%b %e %H:%M", &ut.login_time()).unwrap() // LC_ALL=C } impl Pinky { diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index 0d5543d8b..ad5c083aa 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -234,7 +234,7 @@ impl LineSplitter { fn new(settings: &Settings) -> LineSplitter { LineSplitter { lines_per_split: settings.strategy_param.parse().unwrap_or_else(|_| { - crash!(1, "invalid number of lines: ‘{}’", settings.strategy_param) + crash!(1, "invalid number of lines: '{}'", settings.strategy_param) }), } } diff --git a/src/uu/stat/src/stat.rs b/src/uu/stat/src/stat.rs index 4e1d9d2c9..7bf3db4c2 100644 --- a/src/uu/stat/src/stat.rs +++ b/src/uu/stat/src/stat.rs @@ -24,7 +24,7 @@ use std::{cmp, fs, iter}; macro_rules! check_bound { ($str: ident, $bound:expr, $beg: expr, $end: expr) => { if $end >= $bound { - return Err(format!("‘{}’: invalid directive", &$str[$beg..$end])); + return Err(format!("'{}': invalid directive", &$str[$beg..$end])); } }; } diff --git a/src/uu/test/src/parser.rs b/src/uu/test/src/parser.rs index d4302bd67..5eec781ba 100644 --- a/src/uu/test/src/parser.rs +++ b/src/uu/test/src/parser.rs @@ -167,7 +167,7 @@ impl Parser { self.expr(); match self.next_token() { Symbol::Literal(s) if s == ")" => (), - _ => panic!("expected ‘)’"), + _ => panic!("expected ')'"), } } } @@ -314,7 +314,7 @@ impl Parser { self.expr(); match self.tokens.next() { - Some(token) => Err(format!("extra argument ‘{}’", token.to_string_lossy())), + Some(token) => Err(format!("extra argument '{}'", token.to_string_lossy())), None => Ok(()), } } diff --git a/src/uu/test/src/test.rs b/src/uu/test/src/test.rs index 97a244cdc..107ad2df4 100644 --- a/src/uu/test/src/test.rs +++ b/src/uu/test/src/test.rs @@ -88,7 +88,7 @@ fn eval(stack: &mut Vec) -> Result { return Ok(true); } _ => { - return Err(format!("missing argument after ‘{:?}’", op)); + return Err(format!("missing argument after '{:?}'", op)); } }; @@ -140,7 +140,7 @@ fn eval(stack: &mut Vec) -> Result { } fn integers(a: &OsStr, b: &OsStr, op: &OsStr) -> Result { - let format_err = |value| format!("invalid integer ‘{}’", value); + let format_err = |value| format!("invalid integer '{}'", value); let a = a.to_string_lossy(); let a: i64 = a.parse().map_err(|_| format_err(a))?; @@ -156,7 +156,7 @@ fn integers(a: &OsStr, b: &OsStr, op: &OsStr) -> Result { "-ge" => a >= b, "-lt" => a < b, "-le" => a <= b, - _ => return Err(format!("unknown operator ‘{}’", operator)), + _ => return Err(format!("unknown operator '{}'", operator)), }) } @@ -164,7 +164,7 @@ fn isatty(fd: &OsStr) -> Result { let fd = fd.to_string_lossy(); fd.parse() - .map_err(|_| format!("invalid integer ‘{}’", fd)) + .map_err(|_| format!("invalid integer '{}'", fd)) .map(|i| { #[cfg(not(target_os = "redox"))] unsafe { diff --git a/src/uu/tr/src/tr.rs b/src/uu/tr/src/tr.rs index 3c362dcec..9916af7db 100644 --- a/src/uu/tr/src/tr.rs +++ b/src/uu/tr/src/tr.rs @@ -311,7 +311,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { if !(delete_flag || squeeze_flag) && sets.len() < 2 { show_error!( - "missing operand after ‘{}’\nTry `{} --help` for more information.", + "missing operand after '{}'\nTry `{} --help` for more information.", sets[0], executable!() ); diff --git a/src/uu/truncate/src/truncate.rs b/src/uu/truncate/src/truncate.rs index f81a95ab2..8ef246833 100644 --- a/src/uu/truncate/src/truncate.rs +++ b/src/uu/truncate/src/truncate.rs @@ -210,7 +210,7 @@ fn truncate_reference_and_size( let mode = match parse_mode_and_size(size_string) { Ok(m) => match m { TruncateMode::Absolute(_) => { - crash!(1, "you must specify a relative ‘--size’ with ‘--reference’") + crash!(1, "you must specify a relative '--size' with '--reference'") } _ => m, }, diff --git a/src/uu/who/src/who.rs b/src/uu/who/src/who.rs index 44f565438..047452240 100644 --- a/src/uu/who/src/who.rs +++ b/src/uu/who/src/who.rs @@ -300,7 +300,7 @@ fn idle_string<'a>(when: i64, boottime: i64) -> Cow<'a, str> { } fn time_string(ut: &Utmpx) -> String { - time::strftime("%Y-%m-%d %H:%M", &ut.login_time()).unwrap() + time::strftime("%b %e %H:%M", &ut.login_time()).unwrap() // LC_ALL=C } #[inline] @@ -523,8 +523,8 @@ impl Who { buf.push_str(&msg); } buf.push_str(&format!(" {:<12}", line)); - // "%Y-%m-%d %H:%M" - let time_size = 4 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2; + // "%b %e %H:%M" (LC_ALL=C) + let time_size = 3 + 2 + 2 + 1 + 2; buf.push_str(&format!(" {:<1$}", time, time_size)); if !self.short_output { diff --git a/src/uucore/src/lib/parser/parse_size.rs b/src/uucore/src/lib/parser/parse_size.rs index 58213adef..ec0b08c9e 100644 --- a/src/uucore/src/lib/parser/parse_size.rs +++ b/src/uucore/src/lib/parser/parse_size.rs @@ -18,7 +18,7 @@ use std::fmt; /// /// # Errors /// -/// Will return `ParseSizeError` if it’s not possible to parse this +/// Will return `ParseSizeError` if it's not possible to parse this /// string into a number, e.g. if the string does not begin with a /// numeral, or if the unit is not one of the supported units described /// in the preceding section. @@ -109,19 +109,19 @@ impl fmt::Display for ParseSizeError { impl ParseSizeError { fn parse_failure(s: &str) -> ParseSizeError { - // stderr on linux (GNU coreutils 8.32) + // stderr on linux (GNU coreutils 8.32) (LC_ALL=C) // has to be handled in the respective uutils because strings differ, e.g.: // // `NUM` - // head: invalid number of bytes: ‘1fb’ - // tail: invalid number of bytes: ‘1fb’ + // head: invalid number of bytes: '1fb' + // tail: invalid number of bytes: '1fb' // // `SIZE` - // split: invalid number of bytes: ‘1fb’ - // truncate: Invalid number: ‘1fb’ + // split: invalid number of bytes: '1fb' + // truncate: Invalid number: '1fb' // // `MODE` - // stdbuf: invalid mode ‘1fb’ + // stdbuf: invalid mode '1fb' // // `SIZE` // sort: invalid suffix in --buffer-size argument '1fb' @@ -140,27 +140,27 @@ impl ParseSizeError { // --width // --strings // etc. - ParseSizeError::ParseFailure(format!("‘{}’", s)) + ParseSizeError::ParseFailure(format!("'{}'", s)) } fn size_too_big(s: &str) -> ParseSizeError { - // stderr on linux (GNU coreutils 8.32) + // stderr on linux (GNU coreutils 8.32) (LC_ALL=C) // has to be handled in the respective uutils because strings differ, e.g.: // - // head: invalid number of bytes: ‘1Y’: Value too large for defined data type - // tail: invalid number of bytes: ‘1Y’: Value too large for defined data type - // split: invalid number of bytes: ‘1Y’: Value too large for defined data type - // truncate: Invalid number: ‘1Y’: Value too large for defined data type - // stdbuf: invalid mode ‘1Y’: Value too large for defined data type + // head: invalid number of bytes: '1Y': Value too large for defined data type + // tail: invalid number of bytes: '1Y': Value too large for defined data type + // split: invalid number of bytes: '1Y': Value too large for defined data type + // truncate: Invalid number: '1Y': Value too large for defined data type + // stdbuf: invalid mode '1Y': Value too large for defined data type // sort: -S argument '1Y' too large // du: -B argument '1Y' too large // od: -N argument '1Y' too large // etc. // // stderr on macos (brew - GNU coreutils 8.32) also differs for the same version, e.g.: - // ghead: invalid number of bytes: ‘1Y’: Value too large to be stored in data type - // gtail: invalid number of bytes: ‘1Y’: Value too large to be stored in data type - ParseSizeError::SizeTooBig(format!("‘{}’: Value too large for defined data type", s)) + // ghead: invalid number of bytes: '1Y': Value too large to be stored in data type + // gtail: invalid number of bytes: '1Y': Value too large to be stored in data type + ParseSizeError::SizeTooBig(format!("'{}': Value too large for defined data type", s)) } } @@ -227,7 +227,7 @@ mod tests { )); assert_eq!( - ParseSizeError::SizeTooBig("‘1Y’: Value too large for defined data type".to_string()), + ParseSizeError::SizeTooBig("'1Y': Value too large for defined data type".to_string()), parse_size("1Y").unwrap_err() ); } @@ -262,7 +262,7 @@ mod tests { for &test_string in &test_strings { assert_eq!( parse_size(test_string).unwrap_err(), - ParseSizeError::ParseFailure(format!("‘{}’", test_string)) + ParseSizeError::ParseFailure(format!("'{}'", test_string)) ); } } diff --git a/tests/by-util/test_base32.rs b/tests/by-util/test_base32.rs index 8e3e780c5..38ead28f1 100644 --- a/tests/by-util/test_base32.rs +++ b/tests/by-util/test_base32.rs @@ -103,7 +103,7 @@ fn test_wrap_bad_arg() { .arg(wrap_param) .arg("b") .fails() - .stderr_only("base32: Invalid wrap size: ‘b’: invalid digit found in string\n"); + .stderr_only("base32: Invalid wrap size: 'b': invalid digit found in string\n"); } } @@ -114,7 +114,7 @@ fn test_base32_extra_operand() { .arg("a.txt") .arg("a.txt") .fails() - .stderr_only("base32: extra operand ‘a.txt’"); + .stderr_only("base32: extra operand 'a.txt'"); } #[test] diff --git a/tests/by-util/test_base64.rs b/tests/by-util/test_base64.rs index 236f53fb1..7c7f19205 100644 --- a/tests/by-util/test_base64.rs +++ b/tests/by-util/test_base64.rs @@ -89,7 +89,7 @@ fn test_wrap_bad_arg() { .arg(wrap_param) .arg("b") .fails() - .stderr_only("base64: Invalid wrap size: ‘b’: invalid digit found in string\n"); + .stderr_only("base64: Invalid wrap size: 'b': invalid digit found in string\n"); } } @@ -100,7 +100,7 @@ fn test_base64_extra_operand() { .arg("a.txt") .arg("a.txt") .fails() - .stderr_only("base64: extra operand ‘a.txt’"); + .stderr_only("base64: extra operand 'a.txt'"); } #[test] diff --git a/tests/by-util/test_chown.rs b/tests/by-util/test_chown.rs index c8a8ea538..86365f51b 100644 --- a/tests/by-util/test_chown.rs +++ b/tests/by-util/test_chown.rs @@ -172,14 +172,14 @@ fn test_chown_only_colon() { // expected: // $ chown -v :: file.txt 2>out_err ; echo $? ; cat out_err // 1 - // chown: invalid group: ‘::’ + // chown: invalid group: '::' scene .ucmd() .arg("::") .arg("--verbose") .arg(file1) .fails() - .stderr_contains(&"invalid group: ‘::’"); + .stderr_contains(&"invalid group: '::'"); } #[test] diff --git a/tests/by-util/test_date.rs b/tests/by-util/test_date.rs index 72747fa66..a7a5fa583 100644 --- a/tests/by-util/test_date.rs +++ b/tests/by-util/test_date.rs @@ -117,7 +117,7 @@ fn test_date_format_without_plus() { new_ucmd!() .arg("%s") .fails() - .stderr_contains("date: invalid date ‘%s’") + .stderr_contains("date: invalid date '%s'") .code_is(1); } diff --git a/tests/by-util/test_du.rs b/tests/by-util/test_du.rs index ffe449880..67036be44 100644 --- a/tests/by-util/test_du.rs +++ b/tests/by-util/test_du.rs @@ -355,7 +355,7 @@ fn test_du_no_permission() { let result = scene.ucmd().arg(SUB_DIR_LINKS).run(); // TODO: replace with ".fails()" once `du` is fixed result.stderr_contains( - "du: cannot read directory ‘subdir/links‘: Permission denied (os error 13)", + "du: cannot read directory 'subdir/links': Permission denied (os error 13)", ); #[cfg(target_os = "linux")] diff --git a/tests/by-util/test_head.rs b/tests/by-util/test_head.rs index 2c4b66696..8065cb490 100755 --- a/tests/by-util/test_head.rs +++ b/tests/by-util/test_head.rs @@ -255,21 +255,21 @@ fn test_head_invalid_num() { new_ucmd!() .args(&["-c", "1024R", "emptyfile.txt"]) .fails() - .stderr_is("head: invalid number of bytes: ‘1024R’"); + .stderr_is("head: invalid number of bytes: '1024R'"); new_ucmd!() .args(&["-n", "1024R", "emptyfile.txt"]) .fails() - .stderr_is("head: invalid number of lines: ‘1024R’"); + .stderr_is("head: invalid number of lines: '1024R'"); #[cfg(not(target_pointer_width = "128"))] new_ucmd!() .args(&["-c", "1Y", "emptyfile.txt"]) .fails() - .stderr_is("head: invalid number of bytes: ‘1Y’: Value too large for defined data type"); + .stderr_is("head: invalid number of bytes: '1Y': Value too large for defined data type"); #[cfg(not(target_pointer_width = "128"))] new_ucmd!() .args(&["-n", "1Y", "emptyfile.txt"]) .fails() - .stderr_is("head: invalid number of lines: ‘1Y’: Value too large for defined data type"); + .stderr_is("head: invalid number of lines: '1Y': Value too large for defined data type"); #[cfg(target_pointer_width = "32")] { let sizes = ["1000G", "10T"]; @@ -279,7 +279,7 @@ fn test_head_invalid_num() { .fails() .code_is(1) .stderr_only(format!( - "head: invalid number of bytes: ‘{}’: Value too large for defined data type", + "head: invalid number of bytes: '{}': Value too large for defined data type", size )); } diff --git a/tests/by-util/test_id.rs b/tests/by-util/test_id.rs index b4b929a2c..102ae2aa1 100644 --- a/tests/by-util/test_id.rs +++ b/tests/by-util/test_id.rs @@ -432,7 +432,7 @@ fn check_coreutil_version(util_name: &str, version_expected: &str) -> String { let scene = TestScenario::new(util_name); let version_check = scene .cmd_keepenv(&util_name) - .env("LANGUAGE", "C") + .env("LC_ALL", "C") .arg("--version") .run(); version_check @@ -476,7 +476,7 @@ fn expected_result(args: &[&str]) -> Result { let scene = TestScenario::new(util_name); let result = scene .cmd_keepenv(util_name) - .env("LANGUAGE", "C") + .env("LC_ALL", "C") .args(args) .run(); diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index f8aa4453b..2a6e827f5 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -168,7 +168,7 @@ fn test_ls_width() { .ucmd() .args(&option.split(' ').collect::>()) .fails() - .stderr_only("ls: invalid line width: ‘1a’"); + .stderr_only("ls: invalid line width: '1a'"); } } diff --git a/tests/by-util/test_mv.rs b/tests/by-util/test_mv.rs index 2f35bf5eb..0c33eaf11 100644 --- a/tests/by-util/test_mv.rs +++ b/tests/by-util/test_mv.rs @@ -614,7 +614,7 @@ fn test_mv_overwrite_nonempty_dir() { // Not same error as GNU; the error message is a rust builtin // TODO: test (and implement) correct error message (or at least decide whether to do so) // Current: "mv: couldn't rename path (Directory not empty; from=a; to=b)" - // GNU: "mv: cannot move ‘a’ to ‘b’: Directory not empty" + // GNU: "mv: cannot move 'a' to 'b': Directory not empty" // Verbose output for the move should not be shown on failure let result = ucmd.arg("-vT").arg(dir_a).arg(dir_b).fails(); @@ -638,7 +638,7 @@ fn test_mv_backup_dir() { .arg(dir_b) .succeeds() .stdout_only(format!( - "‘{}’ -> ‘{}’ (backup: ‘{}~’)\n", + "'{}' -> '{}' (backup: '{}~')\n", dir_a, dir_b, dir_b )); @@ -672,7 +672,7 @@ fn test_mv_errors() { // $ at.touch file && at.mkdir dir // $ mv -T file dir - // err == mv: cannot overwrite directory ‘dir’ with non-directory + // err == mv: cannot overwrite directory 'dir' with non-directory scene .ucmd() .arg("-T") @@ -680,13 +680,13 @@ fn test_mv_errors() { .arg(dir) .fails() .stderr_is(format!( - "mv: cannot overwrite directory ‘{}’ with non-directory\n", + "mv: cannot overwrite directory '{}' with non-directory\n", dir )); // $ at.mkdir dir && at.touch file // $ mv dir file - // err == mv: cannot overwrite non-directory ‘file’ with directory ‘dir’ + // err == mv: cannot overwrite non-directory 'file' with directory 'dir' assert!(!scene .ucmd() .arg(dir) @@ -713,7 +713,7 @@ fn test_mv_verbose() { .arg(file_a) .arg(file_b) .succeeds() - .stdout_only(format!("‘{}’ -> ‘{}’\n", file_a, file_b)); + .stdout_only(format!("'{}' -> '{}'\n", file_a, file_b)); at.touch(file_a); scene @@ -723,7 +723,7 @@ fn test_mv_verbose() { .arg(file_b) .succeeds() .stdout_only(format!( - "‘{}’ -> ‘{}’ (backup: ‘{}~’)\n", + "'{}' -> '{}' (backup: '{}~')\n", file_a, file_b, file_b )); } @@ -756,5 +756,5 @@ fn test_mv_permission_error() { // -r--r--r-- 1 user user 0 okt 25 11:21 b // $ // $ mv -v a b -// mv: try to overwrite ‘b’, overriding mode 0444 (r--r--r--)? y -// ‘a’ -> ‘b’ +// mv: try to overwrite 'b', overriding mode 0444 (r--r--r--)? y +// 'a' -> 'b' diff --git a/tests/by-util/test_numfmt.rs b/tests/by-util/test_numfmt.rs index bb29d431e..e64182fcb 100644 --- a/tests/by-util/test_numfmt.rs +++ b/tests/by-util/test_numfmt.rs @@ -35,7 +35,7 @@ fn test_from_iec_i_requires_suffix() { new_ucmd!() .args(&["--from=iec-i", "1024"]) .fails() - .stderr_is("numfmt: missing 'i' suffix in input: ‘1024’ (e.g Ki/Mi/Gi)"); + .stderr_is("numfmt: missing 'i' suffix in input: '1024' (e.g Ki/Mi/Gi)"); } #[test] @@ -123,7 +123,7 @@ fn test_header_error_if_non_numeric() { new_ucmd!() .args(&["--header=two"]) .run() - .stderr_is("numfmt: invalid header value ‘two’"); + .stderr_is("numfmt: invalid header value 'two'"); } #[test] @@ -131,7 +131,7 @@ fn test_header_error_if_0() { new_ucmd!() .args(&["--header=0"]) .run() - .stderr_is("numfmt: invalid header value ‘0’"); + .stderr_is("numfmt: invalid header value '0'"); } #[test] @@ -139,7 +139,7 @@ fn test_header_error_if_negative() { new_ucmd!() .args(&["--header=-3"]) .run() - .stderr_is("numfmt: invalid header value ‘-3’"); + .stderr_is("numfmt: invalid header value '-3'"); } #[test] @@ -187,7 +187,7 @@ fn test_should_report_invalid_empty_number_on_empty_stdin() { .args(&["--from=auto"]) .pipe_in("\n") .run() - .stderr_is("numfmt: invalid number: ‘’\n"); + .stderr_is("numfmt: invalid number: ''\n"); } #[test] @@ -196,7 +196,7 @@ fn test_should_report_invalid_empty_number_on_blank_stdin() { .args(&["--from=auto"]) .pipe_in(" \t \n") .run() - .stderr_is("numfmt: invalid number: ‘’\n"); + .stderr_is("numfmt: invalid number: ''\n"); } #[test] @@ -205,14 +205,14 @@ fn test_should_report_invalid_suffix_on_stdin() { .args(&["--from=auto"]) .pipe_in("1k") .run() - .stderr_is("numfmt: invalid suffix in input: ‘1k’\n"); + .stderr_is("numfmt: invalid suffix in input: '1k'\n"); // GNU numfmt reports this one as “invalid number” new_ucmd!() .args(&["--from=auto"]) .pipe_in("NaN") .run() - .stderr_is("numfmt: invalid suffix in input: ‘NaN’\n"); + .stderr_is("numfmt: invalid suffix in input: 'NaN'\n"); } #[test] @@ -222,7 +222,7 @@ fn test_should_report_invalid_number_with_interior_junk() { .args(&["--from=auto"]) .pipe_in("1x0K") .run() - .stderr_is("numfmt: invalid number: ‘1x0K’\n"); + .stderr_is("numfmt: invalid number: '1x0K'\n"); } #[test] @@ -461,7 +461,7 @@ fn test_delimiter_overrides_whitespace_separator() { .args(&["-d,"]) .pipe_in("1 234,56") .fails() - .stderr_is("numfmt: invalid number: ‘1 234’\n"); + .stderr_is("numfmt: invalid number: '1 234'\n"); } #[test] diff --git a/tests/by-util/test_pinky.rs b/tests/by-util/test_pinky.rs index 8b50ec2bd..bc2833a42 100644 --- a/tests/by-util/test_pinky.rs +++ b/tests/by-util/test_pinky.rs @@ -106,7 +106,7 @@ fn expected_result(args: &[&str]) -> String { #[allow(clippy::needless_borrow)] TestScenario::new(&util_name) .cmd_keepenv(util_name) - .env("LANGUAGE", "C") + .env("LC_ALL", "C") .args(args) .succeeds() .stdout_move_str() diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index a1350534f..229925a1c 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -309,7 +309,7 @@ fn test_split_lines_number() { .args(&["--lines", "2fb", "file"]) .fails() .code_is(1) - .stderr_only("split: invalid number of lines: ‘2fb’"); + .stderr_only("split: invalid number of lines: '2fb'"); } #[test] @@ -318,13 +318,13 @@ fn test_split_invalid_bytes_size() { .args(&["-b", "1024R"]) .fails() .code_is(1) - .stderr_only("split: invalid number of bytes: ‘1024R’"); + .stderr_only("split: invalid number of bytes: '1024R'"); #[cfg(not(target_pointer_width = "128"))] new_ucmd!() .args(&["-b", "1Y"]) .fails() .code_is(1) - .stderr_only("split: invalid number of bytes: ‘1Y’: Value too large for defined data type"); + .stderr_only("split: invalid number of bytes: '1Y': Value too large for defined data type"); #[cfg(target_pointer_width = "32")] { let sizes = ["1000G", "10T"]; @@ -334,7 +334,7 @@ fn test_split_invalid_bytes_size() { .fails() .code_is(1) .stderr_only(format!( - "split: invalid number of bytes: ‘{}’: Value too large for defined data type", + "split: invalid number of bytes: '{}': Value too large for defined data type", size )); } diff --git a/tests/by-util/test_stat.rs b/tests/by-util/test_stat.rs index 37328d5ae..ddf78815f 100644 --- a/tests/by-util/test_stat.rs +++ b/tests/by-util/test_stat.rs @@ -317,7 +317,7 @@ fn expected_result(args: &[&str]) -> String { #[allow(clippy::needless_borrow)] TestScenario::new(&util_name) .cmd_keepenv(util_name) - .env("LANGUAGE", "C") + .env("LC_ALL", "C") .args(args) .succeeds() .stdout_move_str() diff --git a/tests/by-util/test_stdbuf.rs b/tests/by-util/test_stdbuf.rs index fc1b9324a..66892ea0f 100644 --- a/tests/by-util/test_stdbuf.rs +++ b/tests/by-util/test_stdbuf.rs @@ -63,12 +63,12 @@ fn test_stdbuf_invalid_mode_fails() { .args(&[*option, "1024R", "head"]) .fails() .code_is(125) - .stderr_only("stdbuf: invalid mode ‘1024R’"); + .stderr_only("stdbuf: invalid mode '1024R'"); #[cfg(not(target_pointer_width = "128"))] new_ucmd!() .args(&[*option, "1Y", "head"]) .fails() .code_is(125) - .stderr_contains("stdbuf: invalid mode ‘1Y’: Value too large for defined data type"); + .stderr_contains("stdbuf: invalid mode '1Y': Value too large for defined data type"); } } diff --git a/tests/by-util/test_tail.rs b/tests/by-util/test_tail.rs index 8478944e2..e8dd63317 100644 --- a/tests/by-util/test_tail.rs +++ b/tests/by-util/test_tail.rs @@ -364,21 +364,21 @@ fn test_tail_invalid_num() { new_ucmd!() .args(&["-c", "1024R", "emptyfile.txt"]) .fails() - .stderr_is("tail: invalid number of bytes: ‘1024R’"); + .stderr_is("tail: invalid number of bytes: '1024R'"); new_ucmd!() .args(&["-n", "1024R", "emptyfile.txt"]) .fails() - .stderr_is("tail: invalid number of lines: ‘1024R’"); + .stderr_is("tail: invalid number of lines: '1024R'"); #[cfg(not(target_pointer_width = "128"))] new_ucmd!() .args(&["-c", "1Y", "emptyfile.txt"]) .fails() - .stderr_is("tail: invalid number of bytes: ‘1Y’: Value too large for defined data type"); + .stderr_is("tail: invalid number of bytes: '1Y': Value too large for defined data type"); #[cfg(not(target_pointer_width = "128"))] new_ucmd!() .args(&["-n", "1Y", "emptyfile.txt"]) .fails() - .stderr_is("tail: invalid number of lines: ‘1Y’: Value too large for defined data type"); + .stderr_is("tail: invalid number of lines: '1Y': Value too large for defined data type"); #[cfg(target_pointer_width = "32")] { let sizes = ["1000G", "10T"]; @@ -388,7 +388,7 @@ fn test_tail_invalid_num() { .fails() .code_is(1) .stderr_only(format!( - "tail: invalid number of bytes: ‘{}’: Value too large for defined data type", + "tail: invalid number of bytes: '{}': Value too large for defined data type", size )); } diff --git a/tests/by-util/test_test.rs b/tests/by-util/test_test.rs index 36e825f2d..1867927da 100644 --- a/tests/by-util/test_test.rs +++ b/tests/by-util/test_test.rs @@ -165,7 +165,7 @@ fn test_dangling_string_comparison_is_error() { .args(&["missing_something", "="]) .run() .status_code(2) - .stderr_is("test: missing argument after ‘=’"); + .stderr_is("test: missing argument after '='"); } #[test] @@ -265,7 +265,7 @@ fn test_float_inequality_is_error() { .args(&["123.45", "-ge", "6"]) .run() .status_code(2) - .stderr_is("test: invalid integer ‘123.45’"); + .stderr_is("test: invalid integer '123.45'"); } #[test] @@ -283,7 +283,7 @@ fn test_invalid_utf8_integer_compare() { cmd.run() .status_code(2) - .stderr_is("test: invalid integer ‘fo�o’"); + .stderr_is("test: invalid integer 'fo�o'"); let mut cmd = new_ucmd!(); cmd.raw.arg(arg); @@ -291,7 +291,7 @@ fn test_invalid_utf8_integer_compare() { cmd.run() .status_code(2) - .stderr_is("test: invalid integer ‘fo�o’"); + .stderr_is("test: invalid integer 'fo�o'"); } #[test] @@ -674,7 +674,7 @@ fn test_erroneous_parenthesized_expression() { .args(&["a", "!=", "(", "b", "-a", "b", ")", "!=", "c"]) .run() .status_code(2) - .stderr_is("test: extra argument ‘b’"); + .stderr_is("test: extra argument 'b'"); } #[test] diff --git a/tests/by-util/test_truncate.rs b/tests/by-util/test_truncate.rs index 2da59035e..4b2e9e502 100644 --- a/tests/by-util/test_truncate.rs +++ b/tests/by-util/test_truncate.rs @@ -249,7 +249,7 @@ fn test_size_and_reference() { #[test] fn test_error_filename_only() { - // truncate: you must specify either ‘--size’ or ‘--reference’ + // truncate: you must specify either '--size' or '--reference' new_ucmd!().args(&["file"]).fails().stderr_contains( "error: The following required arguments were not provided: --reference @@ -262,15 +262,15 @@ fn test_invalid_numbers() { new_ucmd!() .args(&["-s", "0X", "file"]) .fails() - .stderr_contains("Invalid number: ‘0X’"); + .stderr_contains("Invalid number: '0X'"); new_ucmd!() .args(&["-s", "0XB", "file"]) .fails() - .stderr_contains("Invalid number: ‘0XB’"); + .stderr_contains("Invalid number: '0XB'"); new_ucmd!() .args(&["-s", "0B", "file"]) .fails() - .stderr_contains("Invalid number: ‘0B’"); + .stderr_contains("Invalid number: '0B'"); } #[test] @@ -299,13 +299,13 @@ fn test_truncate_bytes_size() { .args(&["--size", "1024R", "file"]) .fails() .code_is(1) - .stderr_only("truncate: Invalid number: ‘1024R’"); + .stderr_only("truncate: Invalid number: '1024R'"); #[cfg(not(target_pointer_width = "128"))] new_ucmd!() .args(&["--size", "1Y", "file"]) .fails() .code_is(1) - .stderr_only("truncate: Invalid number: ‘1Y’: Value too large for defined data type"); + .stderr_only("truncate: Invalid number: '1Y': Value too large for defined data type"); #[cfg(target_pointer_width = "32")] { let sizes = ["1000G", "10T"]; @@ -315,7 +315,7 @@ fn test_truncate_bytes_size() { .fails() .code_is(1) .stderr_only(format!( - "truncate: Invalid number: ‘{}’: Value too large for defined data type", + "truncate: Invalid number: '{}': Value too large for defined data type", size )); } diff --git a/tests/by-util/test_users.rs b/tests/by-util/test_users.rs index 68bdf9a5e..1bcbdbdc1 100644 --- a/tests/by-util/test_users.rs +++ b/tests/by-util/test_users.rs @@ -17,7 +17,7 @@ fn test_users_check_name() { #[allow(clippy::needless_borrow)] let expected = TestScenario::new(&util_name) .cmd_keepenv(util_name) - .env("LANGUAGE", "C") + .env("LC_ALL", "C") .succeeds() .stdout_move_str(); diff --git a/tests/by-util/test_who.rs b/tests/by-util/test_who.rs index 4907d2306..9315a5956 100644 --- a/tests/by-util/test_who.rs +++ b/tests/by-util/test_who.rs @@ -158,13 +158,12 @@ fn test_users() { let mut v_actual: Vec<&str> = actual.split_whitespace().collect(); let mut v_expect: Vec<&str> = expect.split_whitespace().collect(); - // TODO: `--users` differs from GNU's output on macOS - // Diff < left / right > : - // <"runner console 2021-05-20 22:03 00:08 196\n" - // >"runner console 2021-05-20 22:03 old 196\n" + // TODO: `--users` sometimes differs from GNU's output on macOS (race condition?) + // actual: "runner console Jun 23 06:37 00:34 196\n" + // expect: "runner console Jun 23 06:37 old 196\n" if cfg!(target_os = "macos") { - v_actual.remove(4); - v_expect.remove(4); + v_actual.remove(5); + v_expect.remove(5); } assert_eq!(v_actual, v_expect); @@ -242,7 +241,7 @@ fn expected_result(args: &[&str]) -> String { #[allow(clippy::needless_borrow)] TestScenario::new(&util_name) .cmd_keepenv(util_name) - .env("LANGUAGE", "C") + .env("LC_ALL", "C") .args(args) .succeeds() .stdout_move_str()