295 lines
7.4 KiB
Rust
295 lines
7.4 KiB
Rust
use core::{
|
|
ffi::{c_char, c_int, CStr, VaList},
|
|
fmt,
|
|
};
|
|
|
|
use crate::{
|
|
error::{CIntCountResult, EResult, TryFromExt},
|
|
file::RawFile,
|
|
io::Write,
|
|
};
|
|
|
|
use self::format::{FmtOpts, FmtRadix, FmtSign, FmtSize, FmtSpec};
|
|
|
|
use super::{stdout, FILE};
|
|
|
|
mod float;
|
|
mod format;
|
|
|
|
struct StringWriter {
|
|
buffer: *mut c_char,
|
|
position: usize,
|
|
capacity: usize,
|
|
}
|
|
|
|
struct FdWriter {
|
|
file: RawFile,
|
|
}
|
|
|
|
impl StringWriter {
|
|
pub fn new(buffer: *mut c_char, capacity: usize) -> Self {
|
|
Self {
|
|
buffer,
|
|
capacity,
|
|
position: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Write for StringWriter {
|
|
fn write(&mut self, data: &[u8]) -> EResult<usize> {
|
|
let count = core::cmp::min(self.capacity - self.position, data.len());
|
|
if count > 0 {
|
|
let dst = unsafe {
|
|
core::slice::from_raw_parts_mut(self.buffer.add(self.position) as *mut u8, count)
|
|
};
|
|
dst.copy_from_slice(&data[..count]);
|
|
self.position += count;
|
|
}
|
|
EResult::Ok(count)
|
|
}
|
|
|
|
fn flush(&mut self) -> EResult<()> {
|
|
todo!()
|
|
}
|
|
}
|
|
|
|
impl fmt::Write for StringWriter {
|
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
|
self.write(s.as_bytes()).into_result(|_| fmt::Error, true)?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl FdWriter {
|
|
pub fn new(file: RawFile) -> Self {
|
|
Self { file }
|
|
}
|
|
}
|
|
|
|
impl Write for FdWriter {
|
|
fn write(&mut self, data: &[u8]) -> EResult<usize> {
|
|
self.file.write(data)
|
|
}
|
|
|
|
fn flush(&mut self) -> EResult<()> {
|
|
self.file.flush()
|
|
}
|
|
}
|
|
|
|
impl fmt::Write for FdWriter {
|
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
|
self.write(s.as_bytes()).into_result(|_| fmt::Error, true)?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn printf_inner<W: Write + fmt::Write>(
|
|
output: &mut W,
|
|
format: &[u8],
|
|
mut ap: VaList,
|
|
) -> EResult<usize> {
|
|
let mut fmt = format.into_iter();
|
|
let mut count = 0;
|
|
|
|
while let Some(&c) = fmt.next() {
|
|
if c != b'%' {
|
|
count += output.write(&[c])?;
|
|
continue;
|
|
}
|
|
|
|
let mut opts = FmtOpts::default();
|
|
let mut cur = fmt.next();
|
|
|
|
// Parse flag characters
|
|
while let Some(&c) = cur {
|
|
match c {
|
|
b'#' => opts.alternate = true,
|
|
b'0' => opts.pad_char = b'0',
|
|
b'-' => opts.left_adjust = true,
|
|
b' ' => opts.sign = FmtSign::Space,
|
|
b'+' => opts.sign = FmtSign::Always,
|
|
b'\'' => unimplemented!("The ' flag is not implemented"),
|
|
_ => break,
|
|
}
|
|
cur = fmt.next();
|
|
}
|
|
|
|
// TODO Field width
|
|
while let Some(&c) = cur {
|
|
if c.is_ascii_digit() {
|
|
opts.width *= 10;
|
|
opts.width += (c - b'0') as usize;
|
|
cur = fmt.next();
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
// TODO Precision
|
|
|
|
// Length modifier
|
|
while let Some(&c) = cur {
|
|
match c {
|
|
b'h' => opts.size = opts.size.shorter(),
|
|
b'l' => opts.size = opts.size.longer(),
|
|
b'q' => opts.size = FmtSize::LongLong,
|
|
b'L' => todo!(),
|
|
b'j' => todo!(),
|
|
b'z' | b'Z' => opts.size = FmtSize::Size,
|
|
b't' => todo!(),
|
|
_ => break,
|
|
}
|
|
cur = fmt.next();
|
|
}
|
|
|
|
// Conversion specifier
|
|
let mut spec = None;
|
|
if let Some(&c) = cur {
|
|
match c {
|
|
b'd' | b'i' => spec = Some(FmtSpec::Integer),
|
|
b'o' => spec = Some(FmtSpec::Unsigned(FmtRadix::Octal)),
|
|
b'u' => spec = Some(FmtSpec::Unsigned(FmtRadix::Decimal)),
|
|
b'x' | b'X' => {
|
|
spec = Some(FmtSpec::Unsigned(FmtRadix::Hex(c.is_ascii_uppercase())))
|
|
}
|
|
b'e' | b'E' => spec = Some(FmtSpec::ScientificFloat(c.is_ascii_uppercase())),
|
|
b'f' | b'F' => spec = Some(FmtSpec::Float(c.is_ascii_uppercase())),
|
|
b'g' | b'G' => spec = Some(FmtSpec::AnyFloat(c.is_ascii_uppercase())),
|
|
b'a' | b'A' => unimplemented!("%a/%A are not implemented"),
|
|
b'c' => spec = Some(FmtSpec::Char),
|
|
b's' => spec = Some(FmtSpec::String),
|
|
b'C' => {
|
|
opts.size = FmtSize::Long;
|
|
spec = Some(FmtSpec::Char);
|
|
}
|
|
b'S' => {
|
|
opts.size = FmtSize::Long;
|
|
spec = Some(FmtSpec::String);
|
|
}
|
|
b'p' => spec = Some(FmtSpec::Pointer),
|
|
b'n' => todo!(),
|
|
b'm' => todo!(),
|
|
b'%' => {
|
|
count += output.write(b"%")?;
|
|
continue;
|
|
}
|
|
_ => (),
|
|
}
|
|
}
|
|
|
|
if let Some(spec) = spec {
|
|
count += opts.fmt(spec, output, &mut ap)?;
|
|
}
|
|
}
|
|
|
|
EResult::Ok(count)
|
|
}
|
|
|
|
#[no_mangle]
|
|
unsafe extern "C" fn printf(format: *const c_char, mut args: ...) -> CIntCountResult {
|
|
vfprintf(stdout, format, args.as_va_list())
|
|
}
|
|
|
|
#[no_mangle]
|
|
unsafe extern "C" fn fprintf(
|
|
stream: *mut FILE,
|
|
format: *const c_char,
|
|
mut args: ...
|
|
) -> CIntCountResult {
|
|
vfprintf(stream, format, args.as_va_list())
|
|
}
|
|
|
|
#[no_mangle]
|
|
unsafe extern "C" fn vprintf(format: *const c_char, ap: VaList) -> CIntCountResult {
|
|
vfprintf(stdout, format, ap)
|
|
}
|
|
|
|
#[no_mangle]
|
|
unsafe extern "C" fn vfprintf(
|
|
stream: *mut FILE,
|
|
format: *const c_char,
|
|
ap: VaList,
|
|
) -> CIntCountResult {
|
|
if format.is_null() {
|
|
panic!();
|
|
}
|
|
let stream = stream.as_mut().unwrap();
|
|
let format = CStr::from_ptr(format);
|
|
|
|
let count = printf_inner(stream, format.to_bytes(), ap)?;
|
|
|
|
CIntCountResult(count.try_into().unwrap())
|
|
}
|
|
|
|
#[no_mangle]
|
|
unsafe extern "C" fn sprintf(
|
|
str: *mut c_char,
|
|
format: *const c_char,
|
|
mut args: ...
|
|
) -> CIntCountResult {
|
|
vsnprintf(str, usize::MAX, format, args.as_va_list())
|
|
}
|
|
|
|
#[no_mangle]
|
|
unsafe extern "C" fn snprintf(
|
|
str: *mut c_char,
|
|
len: usize,
|
|
format: *const c_char,
|
|
mut args: ...
|
|
) -> CIntCountResult {
|
|
vsnprintf(str, len, format, args.as_va_list())
|
|
}
|
|
|
|
#[no_mangle]
|
|
unsafe extern "C" fn vsprintf(
|
|
str: *mut c_char,
|
|
format: *const c_char,
|
|
ap: VaList,
|
|
) -> CIntCountResult {
|
|
vsnprintf(str, usize::MAX, format, ap)
|
|
}
|
|
|
|
#[no_mangle]
|
|
unsafe extern "C" fn vsnprintf(
|
|
str: *mut c_char,
|
|
len: usize,
|
|
format: *const c_char,
|
|
ap: VaList,
|
|
) -> CIntCountResult {
|
|
if format.is_null() {
|
|
panic!();
|
|
}
|
|
if len == 0 {
|
|
return CIntCountResult(0);
|
|
}
|
|
if str.is_null() {
|
|
// TODO output the length
|
|
todo!()
|
|
}
|
|
let mut writer = StringWriter::new(str, len);
|
|
let format = CStr::from_ptr(format);
|
|
|
|
let count = printf_inner(&mut writer, format.to_bytes(), ap)?;
|
|
|
|
CIntCountResult(count.try_into().unwrap())
|
|
}
|
|
|
|
#[no_mangle]
|
|
unsafe extern "C" fn dprintf(fd: c_int, format: *const c_char, mut args: ...) -> CIntCountResult {
|
|
vdprintf(fd, format, args.as_va_list())
|
|
}
|
|
|
|
#[no_mangle]
|
|
unsafe extern "C" fn vdprintf(fd: c_int, format: *const c_char, ap: VaList) -> CIntCountResult {
|
|
if format.is_null() {
|
|
panic!();
|
|
}
|
|
let file = RawFile::e_try_from(fd)?;
|
|
let mut writer = FdWriter::new(file);
|
|
let format = CStr::from_ptr(format);
|
|
|
|
let count = printf_inner(&mut writer, format.to_bytes(), ap)?;
|
|
|
|
CIntCountResult(count.try_into().unwrap())
|
|
}
|