use alloc::string::String; use alloc::string::ToString; use alloc::vec::Vec; use core::fmt::Write as CoreWrite; use core::ops::Range; use core::{fmt, ptr, slice, str}; use c_str::CStr; use io::{self, Write}; use platform::types::*; use platform::{self, WriteByte}; use va_list::{VaList, VaPrimitive}; #[derive(PartialEq, Eq)] enum IntKind { Byte, Short, Int, Long, LongLong, IntMax, PtrDiff, Size, } trait IntoUsize { fn into_usize(self) -> usize; fn from_usize(i: usize) -> Self; } macro_rules! impl_intousize { ($($kind:tt;)*) => { $(impl IntoUsize for $kind { fn into_usize(self) -> usize { self as usize } fn from_usize(i: usize) -> Self { i as Self } })* } } impl_intousize! { i32; u32; i64; u64; isize; usize; } impl IntoUsize for *const T { fn into_usize(self) -> usize { self as usize } fn from_usize(i: usize) -> Self { i as Self } } impl IntoUsize for *mut T { fn into_usize(self) -> usize { self as usize } fn from_usize(i: usize) -> Self { i as Self } } impl IntoUsize for f32 { fn into_usize(self) -> usize { self.to_bits() as usize } fn from_usize(i: usize) -> Self { Self::from_bits(i as u32) } } impl IntoUsize for f64 { fn into_usize(self) -> usize { self.to_bits() as usize } fn from_usize(i: usize) -> Self { Self::from_bits(i as u64) } } struct BufferedVaList { list: VaList, buf: Vec, i: usize } impl BufferedVaList { fn new(list: VaList) -> Self { Self { list, buf: Vec::new(), i: 0 } } unsafe fn get(&mut self, i: Option) -> T { match i { None => self.next(), Some(i) => self.index(i), } } unsafe fn next(&mut self) -> T { if self.i >= self.buf.len() { self.buf.push(self.list.get::().into_usize()); } let arg = T::from_usize(self.buf[self.i]); self.i += 1; arg } unsafe fn index(&mut self, i: usize) -> T { while self.buf.len() < i { // Getting a usize here most definitely isn't sane, however, // there's no way to know the type! // Just take this for example: // // printf("%*4$d\n", "hi", 0, "hello", 10); // // This chooses the width 10. How does it know the type of 0 and "hello"? // It clearly can't. self.buf.push(self.list.get::()); } T::from_usize(self.buf[i - 1]) } } unsafe fn pop_int_raw(format: &mut *const u8) -> Option { let mut int = None; while let Some(digit) = (**format as char).to_digit(10) { *format = format.offset(1); if int.is_none() { int = Some(0); } *int.as_mut().unwrap() *= 10; *int.as_mut().unwrap() += digit as usize; } int } unsafe fn pop_int(format: &mut *const u8, ap: &mut BufferedVaList) -> Option { if **format == b'*' { *format = format.offset(1); // Peek ahead for a positional argument: let mut format2 = *format; if let Some(i) = pop_int_raw(&mut format2) { if *format2 == b'$' { *format = format2.offset(1); return Some(ap.index::(i)) } } Some(ap.next::()) } else { pop_int_raw(format) } } unsafe fn fmt_int(fmt: u8, i: I) -> String where I: fmt::Display + fmt::Octal + fmt::LowerHex + fmt::UpperHex { match fmt { b'o' => format!("{:o}", i), b'u' => i.to_string(), b'x' => format!("{:x}", i), b'X' => format!("{:X}", i), _ => panic!("fmt_int should never be called with the fmt {}", fmt) } } fn pad(w: &mut W, current_side: bool, pad_char: u8, range: Range) -> io::Result<()> { if current_side { for _ in range { w.write_all(&[pad_char])?; } } Ok(()) } fn float_string(float: c_double, precision: usize, trim: bool) -> String { let mut string = format!("{:.p$}", float, p = precision); if trim { let truncate = { let mut slice = string.trim_right_matches('0'); if slice.ends_with('.') { slice.len() - 1 } else { slice.len() } }; string.truncate(truncate); } string } fn fmt_float_exp( w: &mut W, exp_fmt: u8, range: Option<(isize, isize)>, trim: bool, precision: usize, mut float: c_double, left: bool, pad_space: usize, pad_zero: usize, ) -> io::Result { let mut exp: isize = 0; while float >= 10.0 || float <= -10.0 { float /= 10.0; exp += 1; } while (float > 0.0 && float < 1.0) || (float > -1.0 && float < 0.0) { float *= 10.0; exp -= 1; } if range.map(|(start, end)| exp >= start && exp < end).unwrap_or(false) { return Ok(false); } 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 mut len = string.len() + 2 + 2.max(exp_len); pad(w, !left, b' ', len..pad_space)?; let bytes = if string.starts_with('-') { w.write_all(&[b'-'])?; &string.as_bytes()[1..] } else { string.as_bytes() }; pad(w, !left, b'0', len..pad_zero)?; w.write_all(bytes)?; write!(w, "{}{:+03}", exp_fmt as char, exp)?; pad(w, left, b' ', len..pad_space)?; Ok(true) } 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, b' ', 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, b'0', string.len()..pad_zero)?; w.write_all(bytes)?; pad(w, left, b' ', string.len()..pad_space)?; Ok(string.len()) } unsafe fn inner_printf(w: W, format: *const c_char, mut ap: VaList) -> io::Result { let mut w = &mut platform::CountingWriter::new(w); let mut ap = BufferedVaList::new(ap); let mut format = format as *const u8; while *format != 0 { if *format != b'%' { w.write_all(&[*format])?; } else { format = format.offset(1); // Peek ahead to maybe specify argument to fetch from let mut index = None; let mut format2 = format; if let Some(i) = pop_int_raw(&mut format2) { if *format2 == b'$' { format = format2.offset(1); index = Some(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 *format { b'#' => alternate = true, b'0' => zero = true, b'-' => left = true, b' ' => sign_reserve = true, b'+' => sign_always = true, _ => break } format = format.offset(1); } // Width and precision: let min_width = pop_int(&mut format, &mut ap).unwrap_or(0); let precision = if *format == b'.' { format = format.offset(1); match pop_int(&mut format, &mut ap) { int@Some(_) => int, None => return Ok(-1) } } else { None }; let pad_space = if zero { 0 } else { min_width }; let pad_zero = if zero { min_width } else { 0 }; // Integer size: let mut kind = IntKind::Int; loop { kind = match *format { b'h' => if kind == IntKind::Short || kind == IntKind::Byte { IntKind::Byte } else { IntKind::Short }, b'j' => IntKind::IntMax, b'l' => if kind == IntKind::Long || kind == IntKind::LongLong { IntKind::LongLong } else { IntKind::Long }, b'q' | b'L' => IntKind::LongLong, b't' => IntKind::PtrDiff, b'z' => IntKind::Size, _ => break, }; format = format.offset(1); } // Finally, type: match *format { b'%' => w.write_all(&[b'%'])?, b'd' | b'i' => { let string = match kind { // VaList does not seem to support these two: // IntKind::Byte => ap.get::(index).to_string(), // IntKind::Short => ap.get::(index).to_string(), IntKind::Byte => (ap.get::(index) as c_char).to_string(), IntKind::Short => (ap.get::(index) as c_short).to_string(), IntKind::Int => ap.get::(index).to_string(), IntKind::Long => ap.get::(index).to_string(), IntKind::LongLong => ap.get::(index).to_string(), IntKind::PtrDiff => ap.get::(index).to_string(), IntKind::Size => ap.get::(index).to_string(), IntKind::IntMax => ap.get::(index).to_string(), }; 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, b' ', 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, b'0', len..precision.unwrap_or(pad_zero))?; if !zero { w.write_all(bytes)?; } pad(w, left, b' ', final_len..pad_space)?; }, b'o' | b'u' | b'x' | b'X' => { let fmt = *format; let string = match kind { // VaList does not seem to support these two: // IntKind::Byte => fmt_int(kind ap.get::(index)), // IntKind::Short => fmt_int(kind ap.get::(index)), IntKind::Byte => fmt_int(fmt, ap.get::(index) as c_uchar), IntKind::Short => fmt_int(fmt, ap.get::(index) as c_ushort), IntKind::Int => fmt_int(fmt, ap.get::(index)), IntKind::Long => fmt_int(fmt, ap.get::(index)), IntKind::LongLong => fmt_int(fmt, ap.get::(index)), IntKind::PtrDiff => fmt_int(fmt, ap.get::(index)), IntKind::Size => fmt_int(fmt, ap.get::(index)), IntKind::IntMax => fmt_int(fmt, ap.get::(index)), }; 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 mut len = string.len(); let mut final_len = string.len().max(precision.unwrap_or(0)) + if alternate && string != "0" { match fmt { b'o' if no_precision => 1, b'x' | b'X' => 2, _ => 0 } } else { 0 }; if zero { len = 0; final_len = 0; } pad(w, !left, b' ', final_len..pad_space)?; if alternate && string != "0" { match fmt { b'o' if no_precision => w.write_all(&[b'0'])?, b'x' => w.write_all(&[b'0', b'x'])?, b'X' => w.write_all(&[b'0', b'X'])?, _ => () } } pad(w, true, b'0', len..precision.unwrap_or(pad_zero))?; if !zero { w.write_all(string.as_bytes())?; } pad(w, left, b' ', final_len..pad_space)?; }, b'e' | b'E' => { let exp_fmt = *format; let mut float = ap.get::(index); let precision = precision.unwrap_or(6); fmt_float_exp(w, exp_fmt, None, false, precision, float, left, pad_space, pad_zero)?; }, b'f' | b'F' => { let mut float = ap.get::(index); let precision = precision.unwrap_or(6); fmt_float_normal(w, false, precision, float, left, pad_space, pad_zero)?; }, b'g' | b'G' => { let exp_fmt = b'E' | (*format & 32); let mut float = ap.get::(index); let precision = precision.unwrap_or(6); if !fmt_float_exp(w, exp_fmt, Some((-4, precision as isize)), true, precision, float, left, pad_space, pad_zero)? { fmt_float_normal(w, true, precision, float, left, pad_space, pad_zero)?; } } b's' => { // if kind == IntKind::Long || kind == IntKind::LongLong, handle *const wchar_t let ptr = ap.get::<*const c_char>(index); if ptr.is_null() { w.write_all(b"(null)")?; } else { let mut len = 0; while *ptr.offset(len) != 0 { len += 1; } let len = (len as usize).min(precision.unwrap_or(::core::usize::MAX)); pad(w, !left, b' ', len..pad_space)?; w.write_all(slice::from_raw_parts(ptr as *const u8, len))?; pad(w, left, b' ', len..pad_space)?; } }, b'c' => { // if kind == IntKind::Long || kind == IntKind::LongLong, handle wint_t let c = ap.get::(index) as c_char; pad(w, !left, b' ', 1..pad_space)?; w.write_all(&[c as u8])?; pad(w, left, b' ', 1..pad_space)?; }, b'p' => { let ptr = ap.get::<*const c_void>(index); 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, b' ', len..pad_space)?; if ptr.is_null() { write!(w, "(nil)")?; } else { write!(w, "0x{:x}", ptr as usize)?; } pad(w, left, b' ', len..pad_space)?; }, b'n' => { let ptr = ap.get::<*mut c_int>(index); *ptr = w.written as c_int; }, _ => return Ok(-1) } } format = format.offset(1); } Ok(w.written as c_int) } pub unsafe fn printf(w: W, format: *const c_char, ap: VaList) -> c_int { inner_printf(w, format, ap).unwrap_or(-1) }