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())
}