diff --git a/src/header/stdio/printf.rs b/src/header/stdio/printf.rs index b825868b85..658695ec17 100644 --- a/src/header/stdio/printf.rs +++ b/src/header/stdio/printf.rs @@ -126,6 +126,67 @@ impl VaArg { => VaArg::pointer(ap.arg::<*const c_void>()), } } + unsafe fn transmute(&self, arg: &PrintfArg) -> VaArg { + // At this point, there are conflicting printf arguments. An + // example of this is: + // ```c + // printf("%1$d %1$lf\n", 5, 0.1); + // ``` + // We handle it just like glibc: We read it from the VaList + // using the *last* argument type, but we transmute it when we + // try to access the other ones. + union Untyped { + c_char: c_char, + c_double: c_double, + c_int: c_int, + c_long: c_long, + c_longlong: c_longlong, + c_short: c_short, + intmax_t: intmax_t, + pointer: *const c_void, + ptrdiff_t: ptrdiff_t, + ssize_t: ssize_t + } + let untyped = match *self { + VaArg::c_char(i) => Untyped { c_char: i }, + VaArg::c_double(i) => Untyped { c_double: i }, + VaArg::c_int(i) => Untyped { c_int: i }, + VaArg::c_long(i) => Untyped { c_long: i }, + VaArg::c_longlong(i) => Untyped { c_longlong: i }, + VaArg::c_short(i) => Untyped { c_short: i }, + VaArg::intmax_t(i) => Untyped { intmax_t: i }, + VaArg::pointer(i) => Untyped { pointer: i }, + VaArg::ptrdiff_t(i) => Untyped { ptrdiff_t: i }, + VaArg::ssize_t(i) => Untyped { ssize_t: i } + }; + match (arg.fmtkind, arg.intkind) { + (FmtKind::Percent, _) => panic!("Can't call transmute on %"), + + (FmtKind::Char, _) | + (FmtKind::Unsigned, IntKind::Byte) | + (FmtKind::Signed, IntKind::Byte) => VaArg::c_char(untyped.c_char), + (FmtKind::Unsigned, IntKind::Short) | + (FmtKind::Signed, IntKind::Short) => VaArg::c_short(untyped.c_short), + (FmtKind::Unsigned, IntKind::Int) | + (FmtKind::Signed, IntKind::Int) => VaArg::c_int(untyped.c_int), + (FmtKind::Unsigned, IntKind::Long) | + (FmtKind::Signed, IntKind::Long) => VaArg::c_long(untyped.c_long), + (FmtKind::Unsigned, IntKind::LongLong) | + (FmtKind::Signed, IntKind::LongLong) => VaArg::c_longlong(untyped.c_longlong), + (FmtKind::Unsigned, IntKind::IntMax) | + (FmtKind::Signed, IntKind::IntMax) => VaArg::intmax_t(untyped.intmax_t), + (FmtKind::Unsigned, IntKind::PtrDiff) | + (FmtKind::Signed, IntKind::PtrDiff) => VaArg::ptrdiff_t(untyped.ptrdiff_t), + (FmtKind::Unsigned, IntKind::Size) | + (FmtKind::Signed, IntKind::Size) => VaArg::ssize_t(untyped.ssize_t), + + (FmtKind::AnyNotation, _) | (FmtKind::Decimal, _) | (FmtKind::Scientific, _) + => VaArg::c_double(untyped.c_double), + + (FmtKind::GetWritten, _) | (FmtKind::Pointer, _) | (FmtKind::String, _) + => VaArg::pointer(untyped.pointer), + } + } } #[derive(Default)] struct VaListCache { @@ -133,8 +194,12 @@ struct VaListCache { i: usize } impl VaListCache { - unsafe fn get(&mut self, i: usize, ap: &mut VaList, arg: Option<&PrintfArg>) -> VaArg { + unsafe fn get(&mut self, i: usize, ap: &mut VaList, default: Option<&PrintfArg>) -> VaArg { if let Some(&arg) = self.args.get(i) { + let mut arg = arg; + if let Some(default) = default { + arg = arg.transmute(default); + } return arg; } while self.args.len() < i { @@ -144,8 +209,8 @@ impl VaListCache { // defaulting to c_int. self.args.push(VaArg::c_int(ap.arg::())) } - self.args.push(match arg { - Some(arg) => VaArg::arg_from(arg, ap), + self.args.push(match default { + Some(default) => VaArg::arg_from(default, ap), None => VaArg::c_int(ap.arg::()) }); self.args[i] diff --git a/tests/expected/stdio/printf.stdout b/tests/expected/stdio/printf.stdout index 19b75259c0..32cc6950e3 100644 --- a/tests/expected/stdio/printf.stdout +++ b/tests/expected/stdio/printf.stdout @@ -26,6 +26,9 @@ Positional madness: 4 3 2 00002 |Fiz |Buz | Fiz| Tot| +int: 5 double: 0.100000 0.200000 0.300000 0.400000 +-1717986918 0.100000 +-1717986918 0.200000 Float madness: 1.234568e+02 diff --git a/tests/stdio/printf.c b/tests/stdio/printf.c index ea7b8dde02..414619a5a9 100644 --- a/tests/stdio/printf.c +++ b/tests/stdio/printf.c @@ -33,6 +33,10 @@ int main(void) { printf("%3$d %2$d %1$d\n", 2, 3, 4); printf("%.*3$d\n", 2, 0, 5); printf("|%-*6$.*5$s|%-*6$.*5$s|%*6$.*5$s|%*6$.*5$s|\n", "Fizz", "Buzz", "FizzBuzz", "TotalBuzz", 3, 8); + printf("int: %*6$d double: %lf %lf %lf %lf\n", 5, 0.1, 0.2, 0.3, 0.4, 10); + printf("%1$d %1$lf\n", 5, 0.1); + printf("%1$d %lf\n", 5, 0.2); + //printf("int: %*6$d no info on middle types\n", 5, 0.1, 0.2, 0.3, 0.4, 10); puts("\nFloat madness:"); printf("%20e\n", 123.456789123);