diff --git a/src/uu/truncate/src/truncate.rs b/src/uu/truncate/src/truncate.rs index 79aa4f3f1..57cc280c3 100644 --- a/src/uu/truncate/src/truncate.rs +++ b/src/uu/truncate/src/truncate.rs @@ -61,10 +61,9 @@ pub mod options { pub static NO_CREATE: &str = "no-create"; pub static REFERENCE: &str = "reference"; pub static SIZE: &str = "size"; + pub static ARG_FILES: &str = "files"; } -static ARG_FILES: &str = "files"; - fn get_usage() -> String { format!("{0} [OPTION]... [FILE]...", executable!()) } @@ -116,21 +115,23 @@ pub fn uumain(args: impl uucore::Args) -> i32 { Arg::with_name(options::REFERENCE) .short("r") .long(options::REFERENCE) + .required_unless(options::SIZE) .help("base the size of each file on the size of RFILE") .value_name("RFILE") ) .arg( Arg::with_name(options::SIZE) .short("s") - .long("size") + .long(options::SIZE) + .required_unless(options::REFERENCE) .help("set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified") .value_name("SIZE") ) - .arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true).min_values(1)) + .arg(Arg::with_name(options::ARG_FILES).value_name("FILE").multiple(true).takes_value(true).required(true).min_values(1)) .get_matches_from(args); let files: Vec = matches - .values_of(ARG_FILES) + .values_of(options::ARG_FILES) .map(|v| v.map(ToString::to_string).collect()) .unwrap_or_default(); @@ -152,8 +153,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 { crash!( 1, "cannot stat '{}': No such file or directory", - reference.unwrap() - ); + reference.unwrap_or("".to_string()) + ); // TODO: fix '--no-create' see test_reference and test_truncate_bytes_size } _ => crash!(1, "{}", e.to_string()), } @@ -209,8 +210,7 @@ fn truncate_reference_and_size( } _ => m, }, - // TODO: handle errors ParseFailure(String)/SizeTooBig(String) - Err(_) => crash!(1, "Invalid number: ‘{}’", size_string), + Err(e) => crash!(1, "Invalid number: {}", e.to_string()), }; let fsize = usize::try_from(metadata(rfilename)?.len()).unwrap(); let tsize = mode.to_size(fsize); @@ -265,7 +265,7 @@ fn truncate_size_only( ) -> std::io::Result<()> { let mode = match parse_mode_and_size(size_string) { Ok(m) => m, - Err(_) => crash!(1, "Invalid number: ‘{}’", size_string), + Err(e) => crash!(1, "Invalid number: {}", e.to_string()), }; for filename in &filenames { let fsize = usize::try_from(metadata(filename)?.len()).unwrap(); @@ -294,7 +294,7 @@ fn truncate( } (Some(rfilename), None) => truncate_reference_file_only(&rfilename, filenames, create), (None, Some(size_string)) => truncate_size_only(&size_string, filenames, create), - (None, None) => crash!(1, "you must specify either --reference or --size"), + (None, None) => crash!(1, "you must specify either --reference or --size"), // this case cannot happen anymore because it's handled by clap } } diff --git a/tests/by-util/test_truncate.rs b/tests/by-util/test_truncate.rs index 5c3f169a1..72f7f780b 100644 --- a/tests/by-util/test_truncate.rs +++ b/tests/by-util/test_truncate.rs @@ -48,7 +48,7 @@ fn test_reference() { // manpage: "A FILE argument that does not exist is created." // TODO: 'truncate' does not create the file in this case, // but should because '--no-create' wasn't specified. - at.touch(FILE1); // TODO: remove this when fixed + at.touch(FILE1); // TODO: remove this when 'no-create' is fixed scene.ucmd().arg("-s").arg("+5KB").arg(FILE1).succeeds(); scene @@ -240,11 +240,18 @@ fn test_size_and_reference() { ); } +#[test] +fn test_error_filename_only() { + // truncate: you must specify either ‘--size’ or ‘--reference’ + new_ucmd!().args(&["file"]).fails().stderr_contains( + "error: The following required arguments were not provided: + --reference + --size ", + ); +} + #[test] fn test_invalid_numbers() { - // TODO For compatibility with GNU, `truncate -s 0X` should cause - // the same error as `truncate -s 0X file`, but currently it returns - // a different error. new_ucmd!() .args(&["-s", "0X", "file"]) .fails() @@ -274,3 +281,36 @@ fn test_reference_with_size_file_not_found() { .fails() .stderr_contains("cannot stat 'a': No such file or directory"); } + +#[test] +fn test_truncate_bytes_size() { + // TODO: this should succeed without error, uncomment when '--no-create' is fixed + // new_ucmd!() + // .args(&["--no-create", "--size", "K", "file"]) + // .succeeds(); + new_ucmd!() + .args(&["--size", "1024R", "file"]) + .fails() + .code_is(1) + .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"); + #[cfg(target_pointer_width = "32")] + { + let sizes = ["1000G", "10T"]; + for size in &sizes { + new_ucmd!() + .args(&["--size", size, "file"]) + .fails() + .code_is(1) + .stderr_only(format!( + "truncate: Invalid number: ‘{}’: Value too large for defined data type", + size + )); + } + } +}