Skip to content

Commit 717a692

Browse files
authored
Merge pull request #7246 from RenjiSann/printf-negative-asterisk
printf: negative asterisk param changes alignment
2 parents fc4f39c + dcc2f1b commit 717a692

File tree

2 files changed

+65
-14
lines changed

2 files changed

+65
-14
lines changed

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

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -314,29 +314,36 @@ 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" 🙃
329331
// > �
330332
// For now, we let printf panic when we truncate within a code point.
331333
// TODO: We need to not use Rust's formatting for aligning the output,
332334
// so that we can just write bytes to stdout without panicking.
333-
let precision = resolve_asterisk(*precision, &mut args)?;
335+
let precision = resolve_asterisk(*precision, &mut args);
334336
let s = args.get_str();
335337
let truncated = match precision {
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();
@@ -374,8 +381,8 @@ impl Spec {
374381
positive_sign,
375382
alignment,
376383
} => {
377-
let width = resolve_asterisk(*width, &mut args)?.unwrap_or(0);
378-
let precision = resolve_asterisk(*precision, &mut args)?.unwrap_or(0);
384+
let width = resolve_asterisk(*width, &mut args).unwrap_or(0);
385+
let precision = resolve_asterisk(*precision, &mut args).unwrap_or(0);
379386
let i = args.get_i64();
380387

381388
if precision as u64 > i32::MAX as u64 {
@@ -397,8 +404,8 @@ impl Spec {
397404
precision,
398405
alignment,
399406
} => {
400-
let width = resolve_asterisk(*width, &mut args)?.unwrap_or(0);
401-
let precision = resolve_asterisk(*precision, &mut args)?.unwrap_or(0);
407+
let width = resolve_asterisk(*width, &mut args).unwrap_or(0);
408+
let precision = resolve_asterisk(*precision, &mut args).unwrap_or(0);
402409
let i = args.get_u64();
403410

404411
if precision as u64 > i32::MAX as u64 {
@@ -423,8 +430,8 @@ impl Spec {
423430
alignment,
424431
precision,
425432
} => {
426-
let width = resolve_asterisk(*width, &mut args)?.unwrap_or(0);
427-
let precision = resolve_asterisk(*precision, &mut args)?.unwrap_or(6);
433+
let width = resolve_asterisk(*width, &mut args).unwrap_or(0);
434+
let precision = resolve_asterisk(*precision, &mut args).unwrap_or(6);
428435
let f = args.get_f64();
429436

430437
if precision as u64 > i32::MAX as u64 {
@@ -450,12 +457,30 @@ impl Spec {
450457
fn resolve_asterisk<'a>(
451458
option: Option<CanAsterisk<usize>>,
452459
mut args: impl ArgumentIter<'a>,
453-
) -> Result<Option<usize>, FormatError> {
454-
Ok(match option {
460+
) -> Option<usize> {
461+
match option {
455462
None => None,
456463
Some(CanAsterisk::Asterisk) => Some(usize::try_from(args.get_u64()).ok().unwrap_or(0)),
457464
Some(CanAsterisk::Fixed(w)) => Some(w),
458-
})
465+
}
466+
}
467+
468+
fn resolve_asterisk_maybe_negative<'a>(
469+
option: Option<CanAsterisk<usize>>,
470+
mut args: impl ArgumentIter<'a>,
471+
) -> Option<(usize, bool)> {
472+
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+
}
459484
}
460485

461486
fn write_padded(

tests/by-util/test_printf.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,32 @@ fn sub_any_asterisk_hex_arg() {
504504
.stdout_only("0123456789");
505505
}
506506

507+
#[test]
508+
fn sub_any_asterisk_negative_first_param() {
509+
new_ucmd!()
510+
.args(&["a(%*s)b", "-5", "xyz"])
511+
.succeeds()
512+
.stdout_only("a(xyz )b"); // Would be 'a( xyz)b' if -5 was 5
513+
514+
// Negative octal
515+
new_ucmd!()
516+
.args(&["a(%*s)b", "-010", "xyz"])
517+
.succeeds()
518+
.stdout_only("a(xyz )b");
519+
520+
// Negative hexadecimal
521+
new_ucmd!()
522+
.args(&["a(%*s)b", "-0x10", "xyz"])
523+
.succeeds()
524+
.stdout_only("a(xyz )b");
525+
526+
// Should also work on %c
527+
new_ucmd!()
528+
.args(&["a(%*c)b", "-5", "x"])
529+
.succeeds()
530+
.stdout_only("a(x )b"); // Would be 'a( x)b' if -5 was 5
531+
}
532+
507533
#[test]
508534
fn sub_any_specifiers_no_params() {
509535
new_ucmd!()

0 commit comments

Comments
 (0)