1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 03:27:44 +00:00

printf: negative asterisk param changes alignement

This commit is contained in:
Dorian Peron 2025-01-31 16:14:24 +01:00
parent 3891ee1159
commit 707e346b84
2 changed files with 55 additions and 4 deletions

View file

@ -314,15 +314,17 @@ impl Spec {
) -> Result<(), FormatError> {
match self {
Self::Char { width, align_left } => {
let width = resolve_asterisk(*width, &mut args)?.unwrap_or(0);
write_padded(writer, &[args.get_char()], width, *align_left)
let (width, neg_width) =
resolve_asterisk_maybe_negative(*width, &mut args)?.unwrap_or_default();
write_padded(writer, &[args.get_char()], width, *align_left || neg_width)
}
Self::String {
width,
align_left,
precision,
} => {
let width = resolve_asterisk(*width, &mut args)?.unwrap_or(0);
let (width, neg_width) =
resolve_asterisk_maybe_negative(*width, &mut args)?.unwrap_or_default();
// GNU does do this truncation on a byte level, see for instance:
// printf "%.1s" 🙃
@ -336,7 +338,12 @@ impl Spec {
Some(p) if p < s.len() => &s[..p],
_ => s,
};
write_padded(writer, truncated.as_bytes(), width, *align_left)
write_padded(
writer,
truncated.as_bytes(),
width,
*align_left || neg_width,
)
}
Self::EscapedString => {
let s = args.get_str();
@ -458,6 +465,24 @@ fn resolve_asterisk<'a>(
})
}
fn resolve_asterisk_maybe_negative<'a>(
option: Option<CanAsterisk<usize>>,
mut args: impl ArgumentIter<'a>,
) -> Result<Option<(usize, bool)>, FormatError> {
Ok(match option {
None => None,
Some(CanAsterisk::Asterisk) => {
let nb = args.get_i64();
if nb < 0 {
Some((usize::try_from(-(nb as isize)).ok().unwrap_or(0), true))
} else {
Some((usize::try_from(nb).ok().unwrap_or(0), false))
}
}
Some(CanAsterisk::Fixed(w)) => Some((w, false)),
})
}
fn write_padded(
mut writer: impl Write,
text: &[u8],

View file

@ -495,6 +495,32 @@ fn sub_any_asterisk_hex_arg() {
.stdout_only("0123456789");
}
#[test]
fn sub_any_asterisk_negative_first_param() {
new_ucmd!()
.args(&["a(%*s)b", "-5", "xyz"])
.succeeds()
.stdout_only("a(xyz )b"); // Would be 'a( xyz)b' if -5 was 5
// Negative octal
new_ucmd!()
.args(&["a(%*s)b", "-010", "xyz"])
.succeeds()
.stdout_only("a(xyz )b");
// Negative hexadecimal
new_ucmd!()
.args(&["a(%*s)b", "-0x10", "xyz"])
.succeeds()
.stdout_only("a(xyz )b");
// Should also work on %c
new_ucmd!()
.args(&["a(%*c)b", "-5", "x"])
.succeeds()
.stdout_only("a(x )b"); // Would be 'a( x)b' if -5 was 5
}
#[test]
fn sub_any_specifiers_no_params() {
new_ucmd!()