Skip to content

Commit 707e346

Browse files
committed
printf: negative asterisk param changes alignement
1 parent 3891ee1 commit 707e346

File tree

2 files changed

+55
-4
lines changed

2 files changed

+55
-4
lines changed

src/uucore/src/lib/features/format/spec.rs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,15 +314,17 @@ impl Spec {
314314
) -> Result<(), FormatError> {
315315
match self {
316316
Self::Char { width, align_left } => {
317-
let width = resolve_asterisk(*width, &mut args)?.unwrap_or(0);
318-
write_padded(writer, &[args.get_char()], width, *align_left)
317+
let (width, neg_width) =
318+
resolve_asterisk_maybe_negative(*width, &mut args)?.unwrap_or_default();
319+
write_padded(writer, &[args.get_char()], width, *align_left || neg_width)
319320
}
320321
Self::String {
321322
width,
322323
align_left,
323324
precision,
324325
} => {
325-
let width = resolve_asterisk(*width, &mut args)?.unwrap_or(0);
326+
let (width, neg_width) =
327+
resolve_asterisk_maybe_negative(*width, &mut args)?.unwrap_or_default();
326328

327329
// GNU does do this truncation on a byte level, see for instance:
328330
// printf "%.1s" 🙃
@@ -336,7 +338,12 @@ impl Spec {
336338
Some(p) if p < s.len() => &s[..p],
337339
_ => s,
338340
};
339-
write_padded(writer, truncated.as_bytes(), width, *align_left)
341+
write_padded(
342+
writer,
343+
truncated.as_bytes(),
344+
width,
345+
*align_left || neg_width,
346+
)
340347
}
341348
Self::EscapedString => {
342349
let s = args.get_str();
@@ -458,6 +465,24 @@ fn resolve_asterisk<'a>(
458465
})
459466
}
460467

468+
fn resolve_asterisk_maybe_negative<'a>(
469+
option: Option<CanAsterisk<usize>>,
470+
mut args: impl ArgumentIter<'a>,
471+
) -> Result<Option<(usize, bool)>, FormatError> {
472+
Ok(match option {
473+
None => None,
474+
Some(CanAsterisk::Asterisk) => {
475+
let nb = args.get_i64();
476+
if nb < 0 {
477+
Some((usize::try_from(-(nb as isize)).ok().unwrap_or(0), true))
478+
} else {
479+
Some((usize::try_from(nb).ok().unwrap_or(0), false))
480+
}
481+
}
482+
Some(CanAsterisk::Fixed(w)) => Some((w, false)),
483+
})
484+
}
485+
461486
fn write_padded(
462487
mut writer: impl Write,
463488
text: &[u8],

tests/by-util/test_printf.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,32 @@ fn sub_any_asterisk_hex_arg() {
495495
.stdout_only("0123456789");
496496
}
497497

498+
#[test]
499+
fn sub_any_asterisk_negative_first_param() {
500+
new_ucmd!()
501+
.args(&["a(%*s)b", "-5", "xyz"])
502+
.succeeds()
503+
.stdout_only("a(xyz )b"); // Would be 'a( xyz)b' if -5 was 5
504+
505+
// Negative octal
506+
new_ucmd!()
507+
.args(&["a(%*s)b", "-010", "xyz"])
508+
.succeeds()
509+
.stdout_only("a(xyz )b");
510+
511+
// Negative hexadecimal
512+
new_ucmd!()
513+
.args(&["a(%*s)b", "-0x10", "xyz"])
514+
.succeeds()
515+
.stdout_only("a(xyz )b");
516+
517+
// Should also work on %c
518+
new_ucmd!()
519+
.args(&["a(%*c)b", "-5", "x"])
520+
.succeeds()
521+
.stdout_only("a(x )b"); // Would be 'a( x)b' if -5 was 5
522+
}
523+
498524
#[test]
499525
fn sub_any_specifiers_no_params() {
500526
new_ucmd!()

0 commit comments

Comments
 (0)