From 4dae902429ee0a54a46cd219526a2493e312c3bf Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Wed, 7 Feb 2024 12:38:25 +0100 Subject: [PATCH 1/3] printf: pad octal numbers with zeros on the left --- .../src/lib/features/format/num_format.rs | 8 ++++-- tests/by-util/test_printf.rs | 28 +++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/uucore/src/lib/features/format/num_format.rs b/src/uucore/src/lib/features/format/num_format.rs index 607c028c32c..819ba6a5f96 100644 --- a/src/uucore/src/lib/features/format/num_format.rs +++ b/src/uucore/src/lib/features/format/num_format.rs @@ -143,11 +143,13 @@ impl Formatter for UnsignedInt { UnsignedIntVariant::Octal(Prefix::No) => format!("{x:o}"), UnsignedIntVariant::Octal(Prefix::Yes) => { // The prefix that rust uses is `0o`, but GNU uses `0`. - // We also need to take into account that 0 should not be 00 + // We also need to take into account that 0 should not be 00 and + // that GNU pads prefixed octals with zeros. + // // Since this is an unsigned int, we do not need to take the minus // sign into account. - if x == 0 { - format!("{x:o}") + if x < 8u64.pow(self.precision.saturating_sub(1) as u32) { + format!("{x:0>width$o}", width = self.precision) } else { format!("0{x:o}") } diff --git a/tests/by-util/test_printf.rs b/tests/by-util/test_printf.rs index db4c5aa7f8d..86d9060ae4a 100644 --- a/tests/by-util/test_printf.rs +++ b/tests/by-util/test_printf.rs @@ -680,3 +680,31 @@ fn char_as_byte() { fn no_infinite_loop() { new_ucmd!().args(&["a", "b"]).succeeds().stdout_only("a"); } + +#[test] +fn pad_octal_with_prefix() { + new_ucmd!() + .args(&[">%#15.6o<", "0"]) + .succeeds() + .stdout_only("> 000000<"); + + new_ucmd!() + .args(&[">%#15.6o<", "01"]) + .succeeds() + .stdout_only("> 000001<"); + + new_ucmd!() + .args(&[">%#15.6o<", "01234"]) + .succeeds() + .stdout_only("> 001234<"); + + new_ucmd!() + .args(&[">%#15.6o<", "012345"]) + .succeeds() + .stdout_only("> 012345<"); + + new_ucmd!() + .args(&[">%#15.6o<", "0123456"]) + .succeeds() + .stdout_only("> 0123456<"); +} From 3126e5f8a114d1d078633263607ae9e085c62d15 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Wed, 7 Feb 2024 16:14:12 +0100 Subject: [PATCH 2/3] printf: fix padding and prefixes for unsigned ints --- .../src/lib/features/format/num_format.rs | 46 ++++++------------- tests/by-util/test_printf.rs | 30 +++++++++++- 2 files changed, 42 insertions(+), 34 deletions(-) diff --git a/src/uucore/src/lib/features/format/num_format.rs b/src/uucore/src/lib/features/format/num_format.rs index 819ba6a5f96..3e9c44f22e1 100644 --- a/src/uucore/src/lib/features/format/num_format.rs +++ b/src/uucore/src/lib/features/format/num_format.rs @@ -140,45 +140,25 @@ impl Formatter for UnsignedInt { fn fmt(&self, mut writer: impl Write, x: Self::Input) -> std::io::Result<()> { let mut s = match self.variant { UnsignedIntVariant::Decimal => format!("{x}"), - UnsignedIntVariant::Octal(Prefix::No) => format!("{x:o}"), - UnsignedIntVariant::Octal(Prefix::Yes) => { - // The prefix that rust uses is `0o`, but GNU uses `0`. - // We also need to take into account that 0 should not be 00 and - // that GNU pads prefixed octals with zeros. - // - // Since this is an unsigned int, we do not need to take the minus - // sign into account. - if x < 8u64.pow(self.precision.saturating_sub(1) as u32) { - format!("{x:0>width$o}", width = self.precision) - } else { - format!("0{x:o}") - } - } - UnsignedIntVariant::Hexadecimal(Case::Lowercase, Prefix::No) => { + UnsignedIntVariant::Octal(_) => format!("{x:o}"), + UnsignedIntVariant::Hexadecimal(Case::Lowercase, _) => { format!("{x:x}") } - UnsignedIntVariant::Hexadecimal(Case::Lowercase, Prefix::Yes) => { - if x == 0 { - "0".to_string() - } else { - format!("{x:#x}") - } - } - UnsignedIntVariant::Hexadecimal(Case::Uppercase, Prefix::No) => { + UnsignedIntVariant::Hexadecimal(Case::Uppercase, _) => { format!("{x:X}") } - UnsignedIntVariant::Hexadecimal(Case::Uppercase, Prefix::Yes) => { - if x == 0 { - "0".to_string() - } else { - format!("{x:#X}") - } - } }; - if self.precision > s.len() { - s = format!("{:0width$}", s, width = self.precision); - } + // Zeroes doe not get a prefix. An octal value does also not get a + // prefix if the padded value will not start with a zero. + let prefix = match (x, self.variant) { + (1.., UnsignedIntVariant::Hexadecimal(Case::Lowercase, Prefix::Yes)) => "0x", + (1.., UnsignedIntVariant::Hexadecimal(Case::Uppercase, Prefix::Yes)) => "0X", + (1.., UnsignedIntVariant::Octal(Prefix::Yes)) if s.len() >= self.precision => "0", + _ => "", + }; + + s = format!("{prefix}{s:0>width$}", width = self.precision); match self.alignment { NumberAlignment::Left => write!(writer, "{s: 0123456<"); } + +#[test] +fn pad_unsigned_zeroes() { + for format in ["%.3u", "%.3x", "%.3X", "%.3o"] { + new_ucmd!() + .args(&[format, "0"]) + .succeeds() + .stdout_only("000"); + } +} + +#[test] +fn pad_unsigned_three() { + for (format, expected) in [ + ("%.3u", "003"), + ("%.3x", "003"), + ("%.3X", "003"), + ("%.3o", "003"), + ("%#.3x", "0x003"), + ("%#.3X", "0X003"), + ("%#.3o", "003"), + ] { + new_ucmd!() + .args(&[format, "3"]) + .succeeds() + .stdout_only(expected); + } +} From 337bfeebc13ac553f6e17cc75eff17306b6a9e1d Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Thu, 8 Feb 2024 16:04:13 +0100 Subject: [PATCH 3/3] uucore/num_format: fix typo in comment --- src/uucore/src/lib/features/format/num_format.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uucore/src/lib/features/format/num_format.rs b/src/uucore/src/lib/features/format/num_format.rs index 3e9c44f22e1..4c4a541c41e 100644 --- a/src/uucore/src/lib/features/format/num_format.rs +++ b/src/uucore/src/lib/features/format/num_format.rs @@ -149,7 +149,7 @@ impl Formatter for UnsignedInt { } }; - // Zeroes doe not get a prefix. An octal value does also not get a + // Zeroes do not get a prefix. An octal value does also not get a // prefix if the padded value will not start with a zero. let prefix = match (x, self.variant) { (1.., UnsignedIntVariant::Hexadecimal(Case::Lowercase, Prefix::Yes)) => "0x",