From 1242acf30423eab53b60920274f1d35d217e5c76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Orhun=20Parmaks=C4=B1z?= Date: Sat, 22 Oct 2022 22:06:25 +0300 Subject: [PATCH 1/6] fix: remove unnecessary out file --- out | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 out diff --git a/out b/out deleted file mode 100644 index e69de29bb..000000000 From 7dc96697c9b20dfbcfe6d3af75797c0d8b6ecd66 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Sun, 2 Jan 2022 22:38:27 -0500 Subject: [PATCH 2/6] split: implement round-robin arg to --number Implement distributing lines of a file in a round-robin manner to a specified number of chunks. For example, $ (seq 1 10 | split -n r/3) && head -v xa[abc] ==> xaa <== 1 4 7 10 ==> xab <== 2 5 8 ==> xac <== 3 6 9 --- src/uu/split/src/split.rs | 44 +++++++++++++++++++++++++++++++++++++ tests/by-util/test_split.rs | 16 ++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index 44447e9b7..365dd279b 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -1221,6 +1221,47 @@ where Ok(()) } +fn split_into_n_chunks_by_line_round_robin( + settings: &Settings, + reader: &mut R, + num_chunks: u64, +) -> UResult<()> +where + R: BufRead, +{ + // This object is responsible for creating the filename for each chunk. + let mut filename_iterator = FilenameIterator::new( + &settings.prefix, + &settings.additional_suffix, + settings.suffix_length, + settings.suffix_type, + settings.suffix_start, + )?; + + // Create one writer for each chunk. This will create each + // of the underlying files (if not in `--filter` mode). + let mut writers = vec![]; + for _ in 0..num_chunks { + let filename = filename_iterator + .next() + .ok_or_else(|| USimpleError::new(1, "output file suffixes exhausted"))?; + let writer = settings.instantiate_current_writer(filename.as_str())?; + writers.push(writer); + } + + let num_chunks: usize = num_chunks.try_into().unwrap(); + for (i, line_result) in reader.lines().enumerate() { + let line = line_result.unwrap(); + let maybe_writer = writers.get_mut(i % num_chunks); + let writer = maybe_writer.unwrap(); + let bytes = line.as_bytes(); + writer.write_all(bytes)?; + writer.write_all(b"\n")?; + } + + Ok(()) +} + fn split(settings: &Settings) -> UResult<()> { let mut reader = BufReader::new(if settings.input == "-" { Box::new(stdin()) as Box @@ -1247,6 +1288,9 @@ fn split(settings: &Settings) -> UResult<()> { let chunk_number = chunk_number - 1; kth_chunk_by_line(settings, &mut reader, chunk_number, num_chunks) } + Strategy::Number(NumberType::RoundRobin(num_chunks)) => { + split_into_n_chunks_by_line_round_robin(settings, &mut reader, num_chunks) + } Strategy::Number(_) => Err(USimpleError::new(1, "-n mode not yet fully implemented")), Strategy::Lines(chunk_size) => { let mut writer = LineChunkWriter::new(chunk_size, settings)?; diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index 7d7bb3929..85f3e7eea 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -743,3 +743,19 @@ fn test_hex_suffix() { assert_eq!(at.read("x0b"), "c"); assert_eq!(at.read("x0c"), ""); } + +#[test] +fn test_round_robin() { + let (at, mut ucmd) = at_and_ucmd!(); + + let file_read = |f| { + let mut s = String::new(); + at.open(f).read_to_string(&mut s).unwrap(); + s + }; + + ucmd.args(&["-n", "r/2", "fivelines.txt"]).succeeds(); + + assert_eq!(file_read("xaa"), "1\n3\n5\n"); + assert_eq!(file_read("xab"), "2\n4\n"); +} From cee6c25cc49c73d2eaf7fb7551943dc0bbb4b4f2 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Mon, 10 Oct 2022 10:41:43 -0400 Subject: [PATCH 3/6] cp: make test for interactive mode more specific --- tests/by-util/test_cp.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 8860a57a5..ecba6addd 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -217,15 +217,18 @@ fn test_cp_target_directory_is_file() { #[test] fn test_cp_arg_interactive() { - new_ucmd!() - .arg(TEST_HELLO_WORLD_SOURCE) - .arg(TEST_HOW_ARE_YOU_SOURCE) - .arg("-i") + let (at, mut ucmd) = at_and_ucmd!(); + at.touch("a"); + at.touch("b"); + // TODO The prompt in GNU cp is different, and it doesn't have the + // response either. + // + // See . + ucmd.args(&["-i", "a", "b"]) .pipe_in("N\n") .succeeds() .no_stdout() - .stderr_contains(format!("overwrite '{}'?", TEST_HOW_ARE_YOU_SOURCE)) - .stderr_contains("Not overwriting"); + .stderr_is("cp: overwrite 'b'? [y/N]: cp: Not overwriting 'b' at user request\n"); } #[test] From cd3f7b89a7088a4c63c511d633f10a64d94af8b3 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Mon, 10 Oct 2022 12:07:17 -0400 Subject: [PATCH 4/6] cp: make cp -a not fail on Windows Before this commit, `cp -a` would terminate with a non-zero status code on Windows because there are no extended attributes (xattr) to copy. However, the GNU documentation for cp states > Try to preserve SELinux security context and extended attributes > (xattr), but ignore any failure to do that and print no > corresponding diagnostic. so it seems reasonable to do nothing instead of exiting with an error in this case. --- src/uu/cp/src/cp.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 299eca9cf..3a1697039 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1146,7 +1146,16 @@ fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResu } #[cfg(not(unix))] { - return Err("XAttrs are only supported on unix.".to_string().into()); + // The documentation for GNU cp states: + // + // > Try to preserve SELinux security context and + // > extended attributes (xattr), but ignore any failure + // > to do that and print no corresponding diagnostic. + // + // so we simply do nothing here. + // + // TODO Silently ignore failures in the `#[cfg(unix)]` + // block instead of terminating immediately on errors. } } }; From ac3fcca6c04f324d5de97efc92712f9960fef5aa Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Thu, 1 Sep 2022 22:12:16 -0400 Subject: [PATCH 5/6] cp: correctly copy ancestor dirs in --parents mode Fix a bug where `cp` failed to copy ancestor directories when using the `--parents` option. For example, before this commit: $ mkdir -p a/b/c d $ cp --parents a/b/c d $ find d d d/c After this commit $ mkdir -p a/b/c d $ cp --parents a/b/c d $ find d d d/a d/a/b d/a/b/c This commit also adds the correct messages for `--verbose` mode: $ cp -r --parents --verbose a/b/c d a -> d/a a/b -> d/a/b 'a/b/c' -> 'd/a/b/c' Fixes #3332. --- src/uu/cp/src/copydir.rs | 21 +++++++++++++++++++++ tests/by-util/test_cp.rs | 13 +++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/uu/cp/src/copydir.rs b/src/uu/cp/src/copydir.rs index e0d6e96db..8930282fb 100644 --- a/src/uu/cp/src/copydir.rs +++ b/src/uu/cp/src/copydir.rs @@ -303,6 +303,27 @@ pub(crate) fn copy_directory( .into()); } + // If in `--parents` mode, create all the necessary ancestor directories. + // + // For example, if the command is `cp --parents a/b/c d`, that + // means we need to copy the two ancestor directories first: + // + // a -> d/a + // a/b -> d/a/b + // + let tmp = if options.parents { + if let Some(parent) = root.parent() { + let new_target = target.join(parent); + std::fs::create_dir_all(&new_target)?; + new_target + } else { + target.to_path_buf() + } + } else { + target.to_path_buf() + }; + let target = tmp.as_path(); + let mut hard_links: Vec<(String, u64)> = vec![]; let preserve_hard_links = options.preserve_hard_links(); diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index ecba6addd..8285905c5 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -1999,6 +1999,19 @@ fn test_copy_same_symlink_no_dereference_dangling() { ucmd.args(&["-d", "a", "b"]).succeeds(); } +#[cfg(not(windows))] +#[test] +fn test_cp_parents_2_dirs() { + let (at, mut ucmd) = at_and_ucmd!(); + at.mkdir_all("a/b/c"); + at.mkdir("d"); + ucmd.args(&["-a", "--parents", "a/b/c", "d"]) + .succeeds() + .no_stderr() + .no_stdout(); + assert!(at.dir_exists("d/a/b/c")); +} + #[test] #[ignore = "issue #3332"] fn test_cp_parents_2() { From dedb6289dd79e264dc20cf672432715e6d7df460 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Mon, 24 Oct 2022 15:13:02 +0200 Subject: [PATCH 6/6] numfmt: handle negative zero values --- src/uu/numfmt/src/format.rs | 6 +++++- tests/by-util/test_numfmt.rs | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/uu/numfmt/src/format.rs b/src/uu/numfmt/src/format.rs index 3550f3bc2..5e61ce794 100644 --- a/src/uu/numfmt/src/format.rs +++ b/src/uu/numfmt/src/format.rs @@ -161,7 +161,11 @@ fn transform_from(s: &str, opts: &TransformOptions) -> Result { remove_suffix(i, suffix, &opts.from).map(|n| { // GNU numfmt doesn't round values if no --from argument is provided by the user if opts.from == Unit::None { - n + if n == -0.0 { + 0.0 + } else { + n + } } else if n < 0.0 { -n.abs().ceil() } else { diff --git a/tests/by-util/test_numfmt.rs b/tests/by-util/test_numfmt.rs index b12ea2d50..72a9e39a9 100644 --- a/tests/by-util/test_numfmt.rs +++ b/tests/by-util/test_numfmt.rs @@ -184,6 +184,11 @@ fn test_negative() { .stdout_is("-1.0Ki\n-1.2Mi\n-103Mi\n"); } +#[test] +fn test_negative_zero() { + new_ucmd!().pipe_in("-0\n-0.0").run().stdout_is("0\n0.0\n"); +} + #[test] fn test_no_op() { new_ucmd!()