From e1ecdbd8a5ef04b2aa40221e6689919eda5583c7 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Mon, 27 Feb 2023 20:44:25 -0700 Subject: [PATCH] Implement vfwprintf --- src/header/stdio/mod.rs | 7 + src/header/wchar/mod.rs | 21 +- src/header/wchar/wprintf.rs | 994 ++++++++++++++++++++++++++++++++++++ 3 files changed, 1015 insertions(+), 7 deletions(-) create mode 100644 src/header/wchar/wprintf.rs diff --git a/src/header/stdio/mod.rs b/src/header/stdio/mod.rs index 05b3af13ee..a5ace892c6 100644 --- a/src/header/stdio/mod.rs +++ b/src/header/stdio/mod.rs @@ -227,6 +227,13 @@ impl FILE { } } + pub fn try_set_wide_orientation_unlocked(&mut self) -> core::result::Result<(), c_int> { + match self.try_set_orientation_unlocked(1) { + 1..=i32::MAX => Ok(()), + x => Err(x), + } + } + pub fn purge(&mut self) { // Purge read buffer self.read_pos = 0; diff --git a/src/header/wchar/mod.rs b/src/header/wchar/mod.rs index f759d17017..8a371a21a7 100644 --- a/src/header/wchar/mod.rs +++ b/src/header/wchar/mod.rs @@ -10,6 +10,8 @@ use crate::{ }; mod utf8; +mod wprintf; + #[repr(C)] #[derive(Clone, Copy)] pub struct mbstate_t; @@ -248,24 +250,29 @@ pub unsafe extern "C" fn ungetwc(wc: wint_t, stream: &mut FILE) -> wint_t { } #[no_mangle] -pub extern "C" fn vfwprintf(stream: *mut FILE, format: *const wchar_t, arg: va_list) -> c_int { - eprintln!("vfwprintf not implemented"); - -1 +pub unsafe extern "C" fn vfwprintf(stream: *mut FILE, format: *const wchar_t, arg: va_list) -> c_int { + let mut stream = (*stream).lock(); + if let Err(_) = (*stream).try_set_wide_orientation_unlocked() { + return -1; + } + + wprintf::wprintf(&mut *stream, format, arg) } #[no_mangle] -pub extern "C" fn vwprintf(format: *const wchar_t, arg: va_list) -> c_int { - eprintln!("vwprintf not implemented"); - -1 +pub unsafe extern "C" fn vwprintf(format: *const wchar_t, arg: va_list) -> c_int { + vfwprintf(&mut *stdout, format, arg) } #[no_mangle] -pub extern "C" fn vswprintf( +pub unsafe extern "C" fn vswprintf( s: *mut wchar_t, n: size_t, format: *const wchar_t, arg: va_list, ) -> c_int { + //TODO: implement vswprintf. This is not as simple as wprintf, since the output is not UTF-8 + // but instead is a wchar array. eprintln!("vswprintf not implemented"); -1 } diff --git a/src/header/wchar/wprintf.rs b/src/header/wchar/wprintf.rs new file mode 100644 index 0000000000..89f02d73d4 --- /dev/null +++ b/src/header/wchar/wprintf.rs @@ -0,0 +1,994 @@ +use crate::io::{self, Write}; +use alloc::{ + collections::BTreeMap, + string::{String, ToString}, + vec::Vec, +}; +use core::{char, cmp, f64, ffi::VaList, fmt, num::FpCategory, ops::Range, slice}; + +use crate::{ + header::errno::EILSEQ, + platform::{self, types::*}, +}; + +// ____ _ _ _ _ +// | __ ) ___ (_) | ___ _ __ _ __ | | __ _| |_ ___ _ +// | _ \ / _ \| | |/ _ \ '__| '_ \| |/ _` | __/ _ (_) +// | |_) | (_) | | | __/ | | |_) | | (_| | || __/_ +// |____/ \___/|_|_|\___|_| | .__/|_|\__,_|\__\___(_) +// |_| + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +enum IntKind { + Byte, + Short, + Int, + Long, + LongLong, + IntMax, + PtrDiff, + Size, +} +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +enum FmtKind { + Percent, + + Signed, + Unsigned, + + Scientific, + Decimal, + AnyNotation, + + String, + Char, + Pointer, + GetWritten, +} +#[derive(Clone, Copy, Debug)] +enum Number { + Static(usize), + Index(usize), + Next, +} +impl Number { + unsafe fn resolve(self, varargs: &mut VaListCache, ap: &mut VaList) -> usize { + let arg = match self { + Number::Static(num) => return num, + Number::Index(i) => varargs.get(i - 1, ap, None), + Number::Next => { + let i = varargs.i; + varargs.i += 1; + varargs.get(i, ap, None) + } + }; + match arg { + VaArg::c_char(i) => i as usize, + VaArg::c_double(i) => i as usize, + VaArg::c_int(i) => i as usize, + VaArg::c_long(i) => i as usize, + VaArg::c_longlong(i) => i as usize, + VaArg::c_short(i) => i as usize, + VaArg::intmax_t(i) => i as usize, + VaArg::pointer(i) => i as usize, + VaArg::ptrdiff_t(i) => i as usize, + VaArg::ssize_t(i) => i as usize, + VaArg::wint_t(i) => i as usize, + } + } +} +#[derive(Clone, Copy, Debug)] +enum VaArg { + 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), + wint_t(wint_t), +} +impl VaArg { + unsafe fn arg_from(fmtkind: FmtKind, intkind: IntKind, ap: &mut VaList) -> VaArg { + // Per the C standard using va_arg with a type with a size + // less than that of an int for integers and double for floats + // is invalid. As a result any arguments smaller than an int or + // double passed to a function will be promoted to the smallest + // possible size. The VaList::arg function will handle this + // automagically. + + match (fmtkind, intkind) { + (FmtKind::Percent, _) => panic!("Can't call arg_from on %"), + + (FmtKind::Char, IntKind::Long) | (FmtKind::Char, IntKind::LongLong) => { + VaArg::wint_t(ap.arg::()) + } + + (FmtKind::Char, _) + | (FmtKind::Unsigned, IntKind::Byte) + | (FmtKind::Signed, IntKind::Byte) => VaArg::c_char(ap.arg::()), + (FmtKind::Unsigned, IntKind::Short) | (FmtKind::Signed, IntKind::Short) => { + VaArg::c_short(ap.arg::()) + } + (FmtKind::Unsigned, IntKind::Int) | (FmtKind::Signed, IntKind::Int) => { + VaArg::c_int(ap.arg::()) + } + (FmtKind::Unsigned, IntKind::Long) | (FmtKind::Signed, IntKind::Long) => { + VaArg::c_long(ap.arg::()) + } + (FmtKind::Unsigned, IntKind::LongLong) | (FmtKind::Signed, IntKind::LongLong) => { + VaArg::c_longlong(ap.arg::()) + } + (FmtKind::Unsigned, IntKind::IntMax) | (FmtKind::Signed, IntKind::IntMax) => { + VaArg::intmax_t(ap.arg::()) + } + (FmtKind::Unsigned, IntKind::PtrDiff) | (FmtKind::Signed, IntKind::PtrDiff) => { + VaArg::ptrdiff_t(ap.arg::()) + } + (FmtKind::Unsigned, IntKind::Size) | (FmtKind::Signed, IntKind::Size) => { + VaArg::ssize_t(ap.arg::()) + } + + (FmtKind::AnyNotation, _) | (FmtKind::Decimal, _) | (FmtKind::Scientific, _) => { + VaArg::c_double(ap.arg::()) + } + + (FmtKind::GetWritten, _) | (FmtKind::Pointer, _) | (FmtKind::String, _) => { + VaArg::pointer(ap.arg::<*const c_void>()) + } + } + } + unsafe fn transmute(&self, fmtkind: FmtKind, intkind: IntKind) -> VaArg { + // At this point, there are conflicting wprintf arguments. An + // example of this is: + // ```c + // wprintf(L"%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, + wint_t: wint_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 }, + VaArg::wint_t(i) => Untyped { wint_t: i }, + }; + match (fmtkind, intkind) { + (FmtKind::Percent, _) => panic!("Can't call transmute on %"), + + (FmtKind::Char, IntKind::Long) | (FmtKind::Char, IntKind::LongLong) => { + VaArg::wint_t(untyped.wint_t) + } + + (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 { + args: Vec, + i: usize, +} +impl VaListCache { + unsafe fn get( + &mut self, + i: usize, + ap: &mut VaList, + default: Option<(FmtKind, IntKind)>, + ) -> VaArg { + if let Some(&arg) = self.args.get(i) { + // This value is already cached + let mut arg = arg; + if let Some((fmtkind, intkind)) = default { + // ...but as a different type + arg = arg.transmute(fmtkind, intkind); + } + return arg; + } + + // Get all values before this value + while self.args.len() < i { + // We can't POSSIBLY know the type if we reach this + // point. Reaching here means there are unused gaps in the + // arguments. Ultimately we'll have to settle down with + // defaulting to c_int. + self.args.push(VaArg::c_int(ap.arg::())) + } + + // Add the value to the cache + self.args.push(match default { + Some((fmtkind, intkind)) => VaArg::arg_from(fmtkind, intkind, ap), + None => VaArg::c_int(ap.arg::()), + }); + + // Return the value + self.args[i] + } +} + +// ___ _ _ _ _ +// |_ _|_ __ ___ _ __ | | ___ _ __ ___ ___ _ __ | |_ __ _| |_(_) ___ _ __ _ +// | || '_ ` _ \| '_ \| |/ _ \ '_ ` _ \ / _ \ '_ \| __/ _` | __| |/ _ \| '_ \(_) +// | || | | | | | |_) | | __/ | | | | | __/ | | | || (_| | |_| | (_) | | | |_ +// |___|_| |_| |_| .__/|_|\___|_| |_| |_|\___|_| |_|\__\__,_|\__|_|\___/|_| |_(_) +// |_| + +enum FmtCase { + Lower, + Upper, +} + +// The spelled-out "infinity"/"INFINITY" is also permitted by the standard +static INF_STR_LOWER: &str = "inf"; +static INF_STR_UPPER: &str = "INF"; + +static NAN_STR_LOWER: &str = "nan"; +static NAN_STR_UPPER: &str = "NAN"; + +unsafe fn pop_int_raw(format: &mut *const u32) -> Option { + let mut int = None; + while let Some(digit) = char::from_u32(**format).unwrap_or('\0').to_digit(10) { + *format = format.add(1); + if int.is_none() { + int = Some(0); + } + *int.as_mut().unwrap() *= 10; + *int.as_mut().unwrap() += digit as usize; + } + int +} +unsafe fn pop_index(format: &mut *const u32) -> Option { + // Peek ahead for a positional argument: + let mut format2 = *format; + if let Some(i) = pop_int_raw(&mut format2) { + if char::from_u32(*format2) == Some('$') { + *format = format2.add(1); + return Some(i); + } + } + None +} +unsafe fn pop_int(format: &mut *const u32) -> Option { + if char::from_u32(**format) == Some('*') { + *format = format.add(1); + Some(pop_index(format).map(Number::Index).unwrap_or(Number::Next)) + } else { + pop_int_raw(format).map(Number::Static) + } +} + +unsafe fn fmt_int(fmt: u32, i: I) -> String +where + I: fmt::Display + fmt::Octal + fmt::LowerHex + fmt::UpperHex, +{ + match char::from_u32(fmt).unwrap_or('\0') { + 'o' => format!("{:o}", i), + 'u' => i.to_string(), + 'x' => format!("{:x}", i), + 'X' => format!("{:X}", i), + _ => panic!( + "fmt_int should never be called with the fmt {:?}", + char::from_u32(fmt) + ), + } +} + +fn pad( + w: &mut W, + current_side: bool, + pad_char: u32, + range: Range, +) -> io::Result<()> { + if current_side { + for _ in range { + if let Some(c) = char::from_u32(pad_char) { + write!(w, "{}", c)?; + } + } + } + Ok(()) +} + +fn abs(float: c_double) -> c_double { + // Don't ask me whe float.abs() seems absent... + if float.is_sign_negative() { + -float + } else { + float + } +} + +fn float_string(float: c_double, precision: usize, trim: bool) -> String { + let mut string = format!("{:.p$}", float, p = precision); + if trim && string.contains('.') { + let truncate = { + let slice = string.trim_end_matches('0'); + let mut truncate = slice.len(); + if slice.ends_with('.') { + truncate -= 1; + } + truncate + }; + string.truncate(truncate); + } + string +} + +fn float_exp(mut float: c_double) -> (c_double, isize) { + let mut exp: isize = 0; + while abs(float) >= 10.0 { + float /= 10.0; + exp += 1; + } + while f64::EPSILON < abs(float) && abs(float) < 1.0 { + float *= 10.0; + exp -= 1; + } + (float, exp) +} + +fn fmt_float_exp( + w: &mut W, + exp_fmt: u32, + trim: bool, + precision: usize, + float: c_double, + exp: isize, + left: bool, + pad_space: usize, + pad_zero: usize, +) -> io::Result<()> { + let mut exp2 = exp; + let mut exp_len = 1; + while exp2 >= 10 { + exp2 /= 10; + exp_len += 1; + } + + let string = float_string(float, precision, trim); + let len = string.len() + 2 + 2.max(exp_len); + + pad(w, !left, ' ' as u32, len..pad_space)?; + let bytes = if string.starts_with('-') { + w.write_all(&[b'-'])?; + &string.as_bytes()[1..] + } else { + string.as_bytes() + }; + pad(w, !left, '0' as u32, len..pad_zero)?; + w.write_all(bytes)?; + if let Some(c) = char::from_u32(exp_fmt) { + write!(w, "{}{:+03}", c, exp)?; + } + pad(w, left, ' ' as u32, len..pad_space)?; + + Ok(()) +} + +fn fmt_float_normal( + w: &mut W, + trim: bool, + precision: usize, + float: c_double, + left: bool, + pad_space: usize, + pad_zero: usize, +) -> io::Result { + let string = float_string(float, precision, trim); + + pad(w, !left, ' ' as u32, string.len()..pad_space)?; + let bytes = if string.starts_with('-') { + w.write_all(&[b'-'])?; + &string.as_bytes()[1..] + } else { + string.as_bytes() + }; + pad(w, true, '0' as u32, string.len()..pad_zero)?; + w.write_all(bytes)?; + pad(w, left, ' ' as u32, string.len()..pad_space)?; + + Ok(string.len()) +} + +/// Write ±infinity or ±NaN representation for any floating-point style +fn fmt_float_nonfinite(w: &mut W, float: c_double, case: FmtCase) -> io::Result<()> { + if float.is_sign_negative() { + w.write_all(&[b'-'])?; + } + + let nonfinite_str = match float.classify() { + FpCategory::Infinite => match case { + FmtCase::Lower => INF_STR_LOWER, + FmtCase::Upper => INF_STR_UPPER, + }, + FpCategory::Nan => match case { + FmtCase::Lower => NAN_STR_LOWER, + FmtCase::Upper => NAN_STR_UPPER, + }, + _ => { + // This function should only be called with infinite or NaN value. + panic!("this should not be possible") + } + }; + + w.write_all(nonfinite_str.as_bytes())?; + + Ok(()) +} + +#[derive(Clone, Copy)] +struct WPrintfIter { + format: *const u32, +} +#[derive(Clone, Copy, Debug)] +struct WPrintfArg { + index: Option, + alternate: bool, + zero: bool, + left: bool, + sign_reserve: bool, + sign_always: bool, + min_width: Number, + precision: Option, + intkind: IntKind, + fmt: u32, + fmtkind: FmtKind, +} +#[derive(Debug)] +enum WPrintfFmt { + Plain(&'static [u32]), + Arg(WPrintfArg), +} +impl Iterator for WPrintfIter { + type Item = Result; + fn next(&mut self) -> Option { + unsafe { + // Send WPrintfFmt::Plain until the next % + let mut len = 0; + while *self.format.add(len) != 0 && char::from_u32(*self.format.add(len)) != Some('%') { + len += 1; + } + if len > 0 { + let slice = slice::from_raw_parts(self.format as *const u32, len); + self.format = self.format.add(len); + return Some(Ok(WPrintfFmt::Plain(slice))); + } + self.format = self.format.add(len); + if *self.format == 0 { + return None; + } + + // *self.format is guaranteed to be '%' at this point + self.format = self.format.add(1); + + let mut peekahead = self.format; + let index = pop_index(&mut peekahead).map(|i| { + self.format = peekahead; + i + }); + + // Flags: + let mut alternate = false; + let mut zero = false; + let mut left = false; + let mut sign_reserve = false; + let mut sign_always = false; + + loop { + match char::from_u32(*self.format).unwrap_or('\0') { + '#' => alternate = true, + '0' => zero = true, + '-' => left = true, + ' ' => sign_reserve = true, + '+' => sign_always = true, + _ => break, + } + self.format = self.format.add(1); + } + + // Width and precision: + let min_width = pop_int(&mut self.format).unwrap_or(Number::Static(0)); + let precision = if char::from_u32(*self.format) == Some('.') { + self.format = self.format.add(1); + match pop_int(&mut self.format) { + int @ Some(_) => int, + None => return Some(Err(())), + } + } else { + None + }; + + // Integer size: + let mut intkind = IntKind::Int; + loop { + intkind = match char::from_u32(*self.format).unwrap_or('\0') { + 'h' => { + if intkind == IntKind::Short || intkind == IntKind::Byte { + IntKind::Byte + } else { + IntKind::Short + } + } + 'j' => IntKind::IntMax, + 'l' => { + if intkind == IntKind::Long || intkind == IntKind::LongLong { + IntKind::LongLong + } else { + IntKind::Long + } + } + 'q' | 'L' => IntKind::LongLong, + 't' => IntKind::PtrDiff, + 'z' => IntKind::Size, + _ => break, + }; + + self.format = self.format.add(1); + } + let fmt = *self.format; + let fmtkind = match char::from_u32(fmt).unwrap_or('\0') { + '%' => FmtKind::Percent, + 'd' | 'i' => FmtKind::Signed, + 'o' | 'u' | 'x' | 'X' => FmtKind::Unsigned, + 'e' | 'E' => FmtKind::Scientific, + 'f' | 'F' => FmtKind::Decimal, + 'g' | 'G' => FmtKind::AnyNotation, + 's' => FmtKind::String, + 'c' => FmtKind::Char, + 'p' => FmtKind::Pointer, + 'n' => FmtKind::GetWritten, + _ => return Some(Err(())), + }; + self.format = self.format.add(1); + + Some(Ok(WPrintfFmt::Arg(WPrintfArg { + index, + alternate, + zero, + left, + sign_reserve, + sign_always, + min_width, + precision, + intkind, + fmt, + fmtkind, + }))) + } + } +} + +unsafe fn inner_wprintf(w: W, format: *const wchar_t, mut ap: VaList) -> io::Result { + let w = &mut platform::CountingWriter::new(w); + + let iterator = WPrintfIter { + format: format as *const u32, + }; + + // Pre-fetch vararg types + let mut varargs = VaListCache::default(); + let mut positional = BTreeMap::new(); + // ^ NOTE: This depends on the sorted order, do not change to HashMap or whatever + + for section in iterator { + let arg = match section { + Ok(WPrintfFmt::Plain(text)) => continue, + Ok(WPrintfFmt::Arg(arg)) => arg, + Err(()) => return Ok(-1), + }; + if arg.fmtkind == FmtKind::Percent { + continue; + } + for num in &[arg.min_width, arg.precision.unwrap_or(Number::Static(0))] { + match num { + Number::Next => varargs.args.push(VaArg::c_int(ap.arg::())), + Number::Index(i) => { + positional.insert(i - 1, (FmtKind::Signed, IntKind::Int)); + } + Number::Static(_) => (), + } + } + match arg.index { + Some(i) => { + positional.insert(i - 1, (arg.fmtkind, arg.intkind)); + } + None => varargs + .args + .push(VaArg::arg_from(arg.fmtkind, arg.intkind, &mut ap)), + } + } + + // Make sure, in order, the positional arguments exist with the specified type + for (i, arg) in positional { + varargs.get(i, &mut ap, Some(arg)); + } + + // Main loop + for section in iterator { + let arg = match section { + Ok(WPrintfFmt::Plain(text)) => { + for &wc in text.iter() { + if let Some(c) = char::from_u32(wc) { + write!(w, "{}", c)?; + } + } + continue; + } + Ok(WPrintfFmt::Arg(arg)) => arg, + Err(()) => return Ok(-1), + }; + let alternate = arg.alternate; + let zero = arg.zero; + let mut left = arg.left; + let sign_reserve = arg.sign_reserve; + let sign_always = arg.sign_always; + let min_width = arg.min_width.resolve(&mut varargs, &mut ap); + let precision = arg.precision.map(|n| n.resolve(&mut varargs, &mut ap)); + let pad_zero = if zero { min_width } else { 0 }; + let signed_space = match pad_zero { + 0 => min_width as isize, + _ => 0, + }; + let pad_space = if signed_space < 0 { + left = true; + -signed_space as usize + } else { + signed_space as usize + }; + let intkind = arg.intkind; + let fmt = arg.fmt; + let fmtkind = arg.fmtkind; + let fmtcase = match char::from_u32(fmt).unwrap_or('\0') { + 'x' | 'f' | 'e' | 'g' => Some(FmtCase::Lower), + 'X' | 'F' | 'E' | 'G' => Some(FmtCase::Upper), + _ => None, + }; + + let index = arg.index.map(|i| i - 1).unwrap_or_else(|| { + if fmtkind == FmtKind::Percent { + 0 + } else { + let i = varargs.i; + varargs.i += 1; + i + } + }); + + match fmtkind { + FmtKind::Percent => w.write_all(&[b'%'])?, + FmtKind::Signed => { + let string = match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) { + VaArg::c_char(i) => i.to_string(), + VaArg::c_double(i) => panic!("this should not be possible"), + VaArg::c_int(i) => i.to_string(), + VaArg::c_long(i) => i.to_string(), + VaArg::c_longlong(i) => i.to_string(), + VaArg::c_short(i) => i.to_string(), + VaArg::intmax_t(i) => i.to_string(), + VaArg::pointer(i) => (i as usize).to_string(), + VaArg::ptrdiff_t(i) => i.to_string(), + VaArg::ssize_t(i) => i.to_string(), + VaArg::wint_t(_) => unreachable!("this should not be possible"), + }; + let positive = !string.starts_with('-'); + let zero = precision == Some(0) && string == "0"; + + let mut len = string.len(); + let mut final_len = string.len().max(precision.unwrap_or(0)); + if positive && (sign_reserve || sign_always) { + final_len += 1; + } + if zero { + len = 0; + final_len = 0; + } + + pad(w, !left, ' ' as u32, final_len..pad_space)?; + + let bytes = if positive { + if sign_reserve { + w.write_all(&[b' '])?; + } else if sign_always { + w.write_all(&[b'+'])?; + } + string.as_bytes() + } else { + w.write_all(&[b'-'])?; + &string.as_bytes()[1..] + }; + pad(w, true, '0' as u32, len..precision.unwrap_or(pad_zero))?; + + if !zero { + w.write_all(bytes)?; + } + + pad(w, left, ' ' as u32, final_len..pad_space)?; + } + FmtKind::Unsigned => { + let string = match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) { + VaArg::c_char(i) => fmt_int(fmt, i as c_uchar), + VaArg::c_double(i) => panic!("this should not be possible"), + VaArg::c_int(i) => fmt_int(fmt, i as c_uint), + VaArg::c_long(i) => fmt_int(fmt, i as c_ulong), + VaArg::c_longlong(i) => fmt_int(fmt, i as c_ulonglong), + VaArg::c_short(i) => fmt_int(fmt, i as c_ushort), + VaArg::intmax_t(i) => fmt_int(fmt, i as uintmax_t), + VaArg::pointer(i) => fmt_int(fmt, i as usize), + VaArg::ptrdiff_t(i) => fmt_int(fmt, i as size_t), + VaArg::ssize_t(i) => fmt_int(fmt, i as size_t), + VaArg::wint_t(_) => unreachable!("this should not be possible"), + }; + let zero = precision == Some(0) && string == "0"; + + // If this int is padded out to be larger than it is, don't + // add an extra zero if octal. + let no_precision = precision.map(|pad| pad < string.len()).unwrap_or(true); + + let len; + let final_len = if zero { + len = 0; + 0 + } else { + len = string.len(); + len.max(precision.unwrap_or(0)) + + if alternate && string != "0" { + match char::from_u32(fmt).unwrap_or('\0') { + 'o' if no_precision => 1, + 'x' | 'X' => 2, + _ => 0, + } + } else { + 0 + } + }; + + pad(w, !left, ' ' as u32, final_len..pad_space)?; + + if alternate && string != "0" { + match char::from_u32(fmt).unwrap_or('\0') { + 'o' if no_precision => w.write_all(&[b'0'])?, + 'x' => w.write_all(&[b'0', b'x'])?, + 'X' => w.write_all(&[b'0', b'X'])?, + _ => (), + } + } + pad(w, true, '0' as u32, len..precision.unwrap_or(pad_zero))?; + + if !zero { + w.write_all(string.as_bytes())?; + } + + pad(w, left, ' ' as u32, final_len..pad_space)?; + } + FmtKind::Scientific => { + let float = match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) { + VaArg::c_double(i) => i, + _ => panic!("this should not be possible"), + }; + if float.is_finite() { + let (float, exp) = float_exp(float); + let precision = precision.unwrap_or(6); + + fmt_float_exp( + w, fmt, false, precision, float, exp, left, pad_space, pad_zero, + )?; + } else { + fmt_float_nonfinite(w, float, fmtcase.unwrap())?; + } + } + FmtKind::Decimal => { + let float = match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) { + VaArg::c_double(i) => i, + _ => panic!("this should not be possible"), + }; + if float.is_finite() { + let precision = precision.unwrap_or(6); + + fmt_float_normal(w, false, precision, float, left, pad_space, pad_zero)?; + } else { + fmt_float_nonfinite(w, float, fmtcase.unwrap())?; + } + } + FmtKind::AnyNotation => { + let float = match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) { + VaArg::c_double(i) => i, + _ => panic!("this should not be possible"), + }; + if float.is_finite() { + let (log, exp) = float_exp(float); + let exp_fmt = ('E' as u32) | (fmt & 32); + let precision = precision.unwrap_or(6); + let use_exp_format = exp < -4 || exp >= precision as isize; + + if use_exp_format { + // Length of integral part will always be 1 here, + // because that's how x/floor(log10(x)) works + let precision = precision.saturating_sub(1); + fmt_float_exp( + w, exp_fmt, true, precision, log, exp, left, pad_space, pad_zero, + )?; + } else { + // Length of integral part will be the exponent of + // the unused logarithm, unless the exponent is + // negative which in case the integral part must + // of course be 0, 1 in length + let len = 1 + cmp::max(0, exp) as usize; + let precision = precision.saturating_sub(len); + fmt_float_normal(w, true, precision, float, left, pad_space, pad_zero)?; + } + } else { + fmt_float_nonfinite(w, float, fmtcase.unwrap())?; + } + } + FmtKind::String => { + let ptr = match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) { + VaArg::pointer(p) => p, + _ => panic!("this should not be possible"), + } as *const c_char; + + if ptr.is_null() { + w.write_all(b"(null)")?; + } else { + let max = precision.unwrap_or(::core::usize::MAX); + + if intkind == IntKind::Long || intkind == IntKind::LongLong { + // Handle wchar_t + let mut ptr = ptr as *const wchar_t; + let mut string = String::new(); + + while *ptr != 0 { + let c = match char::from_u32(*ptr as _) { + Some(c) => c, + None => { + platform::errno = EILSEQ; + return Err(io::last_os_error()); + } + }; + if string.len() + c.len_utf8() >= max { + break; + } + string.push(c); + ptr = ptr.add(1); + } + + pad(w, !left, ' ' as u32, string.len()..pad_space)?; + w.write_all(string.as_bytes())?; + pad(w, left, ' ' as u32, string.len()..pad_space)?; + } else { + let mut len = 0; + while *ptr.add(len) != 0 && len < max { + len += 1; + } + + pad(w, !left, ' ' as u32, len..pad_space)?; + w.write_all(slice::from_raw_parts(ptr as *const u8, len))?; + pad(w, left, ' ' as u32, len..pad_space)?; + } + } + } + FmtKind::Char => match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) { + VaArg::c_char(c) => { + pad(w, !left, ' ' as u32, 1..pad_space)?; + w.write_all(&[c as u8])?; + pad(w, left, ' ' as u32, 1..pad_space)?; + } + VaArg::wint_t(c) => { + let c = match char::from_u32(c as _) { + Some(c) => c, + None => { + platform::errno = EILSEQ; + return Err(io::last_os_error()); + } + }; + let mut buf = [0; 4]; + + pad(w, !left, ' ' as u32, 1..pad_space)?; + w.write_all(c.encode_utf8(&mut buf).as_bytes())?; + pad(w, left, ' ' as u32, 1..pad_space)?; + } + _ => unreachable!("this should not be possible"), + }, + FmtKind::Pointer => { + let ptr = match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) { + VaArg::pointer(p) => p, + _ => panic!("this should not be possible"), + }; + + let mut len = 1; + if ptr.is_null() { + len = "(nil)".len(); + } else { + let mut ptr = ptr as usize; + while ptr >= 10 { + ptr /= 10; + len += 1; + } + } + + pad(w, !left, ' ' as u32, len..pad_space)?; + if ptr.is_null() { + write!(w, "(nil)")?; + } else { + write!(w, "0x{:x}", ptr as usize)?; + } + pad(w, left, ' ' as u32, len..pad_space)?; + } + FmtKind::GetWritten => { + let ptr = match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) { + VaArg::pointer(p) => p, + _ => panic!("this should not be possible"), + }; + + match intkind { + IntKind::Byte => *(ptr as *mut c_char) = w.written as c_char, + IntKind::Short => *(ptr as *mut c_short) = w.written as c_short, + IntKind::Int => *(ptr as *mut c_int) = w.written as c_int, + IntKind::Long => *(ptr as *mut c_long) = w.written as c_long, + IntKind::LongLong => *(ptr as *mut c_longlong) = w.written as c_longlong, + IntKind::IntMax => *(ptr as *mut intmax_t) = w.written as intmax_t, + IntKind::PtrDiff => *(ptr as *mut ptrdiff_t) = w.written as ptrdiff_t, + IntKind::Size => *(ptr as *mut size_t) = w.written as size_t, + } + } + } + } + Ok(w.written as c_int) +} + +pub unsafe fn wprintf(w: W, format: *const wchar_t, ap: VaList) -> c_int { + inner_wprintf(w, format, ap).unwrap_or(-1) +}