mirror of
https://github.com/uutils/coreutils
synced 2024-12-13 23:02:38 +00:00
Merge branch 'main' into rm-correct-prompts
This commit is contained in:
commit
9069d4832a
8 changed files with 123 additions and 8 deletions
0
out
0
out
|
@ -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();
|
||||
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -161,7 +161,11 @@ fn transform_from(s: &str, opts: &TransformOptions) -> Result<f64> {
|
|||
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 {
|
||||
|
|
|
@ -1221,6 +1221,47 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn split_into_n_chunks_by_line_round_robin<R>(
|
||||
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<dyn Read>
|
||||
|
@ -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)?;
|
||||
|
|
|
@ -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 <https://github.com/uutils/coreutils/issues/4023>.
|
||||
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]
|
||||
|
@ -1996,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() {
|
||||
|
|
|
@ -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!()
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue