diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index 50e30721c..f24b8be68 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -38,6 +38,8 @@ struct SeqOptions { } enum Number { + /// Negative zero, as if it were an integer. + MinusZero, BigInt(BigInt), F64(f64), } @@ -45,6 +47,7 @@ enum Number { impl Number { fn is_zero(&self) -> bool { match self { + Number::MinusZero => true, Number::BigInt(n) => n.is_zero(), Number::F64(n) => n.is_zero(), } @@ -52,6 +55,7 @@ impl Number { fn into_f64(self) -> f64 { match self { + Number::MinusZero => -0., // BigInt::to_f64() can not return None. Number::BigInt(n) => n.to_f64().unwrap(), Number::F64(n) => n, @@ -67,7 +71,16 @@ impl FromStr for Number { } match s.parse::() { - Ok(n) => Ok(Number::BigInt(n)), + Ok(n) => { + // If `s` is '-0', then `parse()` returns + // `BigInt::zero()`, but we need to return + // `Number::MinusZero` instead. + if n == BigInt::zero() && s.starts_with('-') { + Ok(Number::MinusZero) + } else { + Ok(Number::BigInt(n)) + } + } Err(_) => match s.parse::() { Ok(value) if value.is_nan() => Err(format!( "invalid 'not-a-number' argument: '{}'\nTry '{} --help' for more information.", @@ -147,6 +160,14 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } match (first, last, increment) { + (Number::MinusZero, Number::BigInt(last), Number::BigInt(increment)) => print_seq_integers( + (BigInt::zero(), increment, last), + options.separator, + options.terminator, + options.widths, + padding, + true, + ), (Number::BigInt(first), Number::BigInt(last), Number::BigInt(increment)) => { print_seq_integers( (first, increment, last), @@ -154,6 +175,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { options.terminator, options.widths, padding, + false, ) } (first, last, increment) => print_seq( @@ -247,13 +269,27 @@ fn print_seq( crash_if_err!(1, stdout().flush()); } -/// BigInt based code path +/// Print an integer sequence. +/// +/// This function prints a sequence of integers defined by `range`, +/// which defines the first integer, last integer, and increment of the +/// range. The `separator` is inserted between each integer and +/// `terminator` is inserted at the end. +/// +/// The `pad` parameter indicates whether to pad numbers to the width +/// given in `padding`. +/// +/// If `is_first_minus_zero` is `true`, then the `first` parameter is +/// printed as if it were negative zero, even though no such number +/// exists as an integer (negative zero only exists for floating point +/// numbers). Only set this to `true` if `first` is actually zero. fn print_seq_integers( range: RangeInt, separator: String, terminator: String, pad: bool, padding: usize, + is_first_minus_zero: bool, ) { let (first, increment, last) = range; let mut value = first; @@ -262,6 +298,9 @@ fn print_seq_integers( if !is_first_iteration { print!("{}", separator); } + if is_first_iteration && is_first_minus_zero { + print!("-"); + } is_first_iteration = false; if pad { print!("{number:>0width$}", number = value, width = padding); diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index eab7e16ce..48221eaf2 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -140,3 +140,21 @@ fn test_seq_wrong_arg_floats() { fn test_zero_step_floats() { new_ucmd!().args(&["10.0", "0", "32"]).fails(); } + +#[test] +fn test_preserve_negative_zero_start() { + new_ucmd!() + .args(&["-0", "1"]) + .succeeds() + .stdout_is("-0\n1\n") + .no_stderr(); +} + +#[test] +fn test_drop_negative_zero_end() { + new_ucmd!() + .args(&["1", "-1", "-0"]) + .succeeds() + .stdout_is("1\n0\n") + .no_stderr(); +}