diff --git a/src/uu/truncate/src/truncate.rs b/src/uu/truncate/src/truncate.rs index df42d7f66..42bd42ab5 100644 --- a/src/uu/truncate/src/truncate.rs +++ b/src/uu/truncate/src/truncate.rs @@ -31,18 +31,38 @@ impl TruncateMode { /// /// `fsize` is the size of the reference file, in bytes. /// + /// If the mode is [`TruncateMode::Reduce`] and the value to + /// reduce by is greater than `fsize`, then this function returns + /// 0 (since it cannot return a negative number). + /// /// # Examples /// + /// Extending a file of 10 bytes by 5 bytes: + /// /// ```rust,ignore /// let mode = TruncateMode::Extend(5); /// let fsize = 10; /// assert_eq!(mode.to_size(fsize), 15); /// ``` + /// + /// Reducing a file by more than its size results in 0: + /// + /// ```rust,ignore + /// let mode = TruncateMode::Reduce(5); + /// let fsize = 3; + /// assert_eq!(mode.to_size(fsize), 0); + /// ``` fn to_size(&self, fsize: usize) -> usize { match self { TruncateMode::Absolute(size) => *size, TruncateMode::Extend(size) => fsize + size, - TruncateMode::Reduce(size) => fsize - size, + TruncateMode::Reduce(size) => { + if *size > fsize { + 0 + } else { + fsize - size + } + } TruncateMode::AtMost(size) => fsize.min(*size), TruncateMode::AtLeast(size) => fsize.max(*size), TruncateMode::RoundDown(size) => fsize - fsize % size, @@ -377,4 +397,11 @@ mod tests { assert_eq!(parse_mode_and_size("/10"), Ok(TruncateMode::RoundDown(10))); assert_eq!(parse_mode_and_size("%10"), Ok(TruncateMode::RoundUp(10))); } + + #[test] + fn test_to_size() { + assert_eq!(TruncateMode::Extend(5).to_size(10), 15); + assert_eq!(TruncateMode::Reduce(5).to_size(10), 5); + assert_eq!(TruncateMode::Reduce(5).to_size(3), 0); + } } diff --git a/tests/by-util/test_truncate.rs b/tests/by-util/test_truncate.rs index 135c55456..801951548 100644 --- a/tests/by-util/test_truncate.rs +++ b/tests/by-util/test_truncate.rs @@ -377,3 +377,15 @@ fn test_division_by_zero_reference_and_size() { .no_stdout() .stderr_contains("division by zero"); } + +/// Test that truncate with a relative size less than 0 is not an error. +#[test] +fn test_underflow_relative_size() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.args(&["-s-1", FILE1]) + .succeeds() + .no_stdout() + .no_stderr(); + assert!(at.file_exists(FILE1)); + assert!(at.read_bytes(FILE1).is_empty()); +}