2024-11-15 20:37:59 +02:00

296 lines
6.9 KiB
Rust

// get*
use core::{
ffi::{c_char, c_int},
ptr::{null_mut, NonNull},
};
use crate::{
allocator,
error::{CEofResult, CIsizeResult, CPtrResult, CResult, EResult},
headers::errno,
io::{
managed::{stdin, stdout, FILE},
Read, Write,
},
util::{PointerExt, PointerStrExt},
};
#[no_mangle]
unsafe extern "C" fn fgetc(fp: *mut FILE) -> CEofResult {
let fp = fp.ensure_mut();
let mut buf = [0];
fp.read_exact(&mut buf)?;
CEofResult::success(buf[0] as _)
}
#[no_mangle]
unsafe extern "C" fn fgets(s: *mut c_char, size: c_int, fp: *mut FILE) -> CPtrResult<c_char> {
let fp = fp.ensure_mut();
let mut s = NonNull::new(s).unwrap();
if size <= 0 {
return CPtrResult::ERROR;
}
// Nothing to read
if size == 1 {
*s.as_mut() = 0;
return CPtrResult::success(s);
}
let size = size as usize;
let mut pos = 0;
let mut buf = [0];
let slice = NonNull::slice_from_raw_parts(s, size).as_mut();
while pos < size - 1 {
let ch = match fp.read(&mut buf)? {
1 => buf[0],
_ => break,
};
slice[pos] = ch as _;
pos += 1;
if ch == b'\n' {
break;
}
}
if pos == 0 {
CPtrResult::ERROR
} else {
slice[pos] = 0;
CPtrResult::success(s)
}
}
#[no_mangle]
unsafe extern "C" fn getc(fp: *mut FILE) -> CEofResult {
fgetc(fp)
}
#[no_mangle]
unsafe extern "C" fn getchar() -> CEofResult {
fgetc(stdin)
}
#[no_mangle]
unsafe extern "C" fn fgetc_unlocked(fp: *mut FILE) -> CEofResult {
let fp = fp.ensure_mut();
let mut buf = [0];
match fp.read_unlocked(&mut buf)? {
1 => CEofResult::success(buf[0] as _),
_ => CEofResult::ERROR,
}
}
#[no_mangle]
unsafe extern "C" fn getc_unlocked(fp: *mut FILE) -> CEofResult {
fgetc_unlocked(fp)
}
#[no_mangle]
unsafe extern "C" fn getchar_unlocked() -> CEofResult {
getc_unlocked(stdin)
}
// put*
#[no_mangle]
unsafe extern "C" fn fputc(ch: c_int, fp: *mut FILE) -> CEofResult {
let fp = fp.ensure_mut();
let ch = ch as u8;
fp.write_all(&[ch])?;
CEofResult::success(ch as c_int)
}
#[no_mangle]
unsafe extern "C" fn fputs(str: *const c_char, fp: *mut FILE) -> CEofResult {
let fp = fp.ensure_mut();
let str = str.ensure_cstr();
fp.write_all(str.to_bytes())?;
CEofResult::success(0)
}
#[no_mangle]
unsafe extern "C" fn putc(ch: c_int, fp: *mut FILE) -> CEofResult {
fputc(ch, fp)
}
#[no_mangle]
unsafe extern "C" fn putchar(ch: c_int) -> CEofResult {
fputc(ch, stdout)
}
#[no_mangle]
unsafe extern "C" fn putc_unlocked(ch: c_int, fp: *mut FILE) -> CEofResult {
let fp = fp.ensure_mut();
let ch = ch as u8;
match fp.write_unlocked(&[ch])? {
1 => CEofResult::success(ch as _),
_ => CEofResult::ERROR,
}
}
#[no_mangle]
unsafe extern "C" fn putchar_unlocked(ch: c_int) -> CEofResult {
putc_unlocked(ch, stdout)
}
#[no_mangle]
unsafe extern "C" fn puts(str: *const c_char) -> CEofResult {
let str = str.ensure_cstr();
let out = match stdout.as_mut() {
Some(out) => out,
None => {
// TODO this hack is needed because currently global constructors are called
// before ygglibc is entered and I/O streams are initialized.
// this will be removed when I properly implement passing DT_INIT_*** ranges
// from dyn-loader to ygglibc.
use yggdrasil_rt::io::RawFd;
unsafe {
yggdrasil_rt::sys::write(RawFd::STDOUT, str.to_bytes()).ok();
yggdrasil_rt::sys::write(RawFd::STDOUT, b"\n").ok();
}
return CEofResult::success(0)
},
};
out.write_all(str.to_bytes())?;
out.write_all(b"\n")?;
CEofResult::success(0)
}
// ungetc
#[no_mangle]
unsafe extern "C" fn ungetc(ch: c_int, fp: *mut FILE) -> CEofResult {
let fp = fp.ensure_mut();
fp.ungetc(ch as _)?;
CEofResult::success(ch)
}
// getdelim
struct MallocBufferWriter {
buffer: Option<NonNull<c_char>>,
capacity: usize,
position: usize,
}
impl MallocBufferWriter {
unsafe fn new(buffer: Option<NonNull<c_char>>, mut capacity: usize) -> Self {
if buffer.is_none() {
capacity = 0;
}
Self {
buffer,
capacity,
position: 0,
}
}
fn try_reserve(&mut self) -> EResult<&mut c_char> {
if self.position == self.capacity {
self.capacity = (self.capacity + 64) & !63;
self.buffer = Some(
unsafe { allocator::c_realloc(self.buffer.map(NonNull::cast), self.capacity) }?
.cast(),
);
}
let buffer = self.buffer.unwrap();
EResult::Ok(unsafe { buffer.add(self.position).as_mut() })
}
fn putc(&mut self, ch: c_int) -> EResult<()> {
let item = self.try_reserve()?;
*item = ch as _;
self.position += 1;
EResult::Ok(())
}
}
fn getdelim_inner(
buffer: Option<NonNull<c_char>>,
capacity: usize,
delim: c_int,
stream: &mut FILE,
) -> (MallocBufferWriter, EResult<()>) {
let mut writer = unsafe { MallocBufferWriter::new(buffer, capacity) };
let mut buf = [0];
loop {
let ch = match stream.read(&mut buf) {
EResult::Ok(1) => buf[0] as c_int,
EResult::Ok(_) => break,
EResult::Err(err) => return (writer, EResult::Err(err)),
};
match writer.putc(ch) {
EResult::Ok(()) => (),
EResult::Err(err) => {
return (writer, EResult::Err(err));
}
}
if ch == delim {
break;
}
}
if writer.position == 0 {
// EOF reached before anything could be read
return (writer, EResult::Err(errno::ESUCCESS));
}
match writer.putc(0) {
EResult::Ok(()) => (),
EResult::Err(err) => {
return (writer, EResult::Err(err));
}
}
(writer, EResult::Ok(()))
}
#[no_mangle]
unsafe extern "C" fn getdelim(
lineptr: *mut *mut c_char,
n: *mut usize,
delim: c_int,
fp: *mut FILE,
) -> CIsizeResult {
let lineptr = lineptr.ensure_mut();
let buffer = NonNull::new(*lineptr);
let n = n.ensure_mut();
let fp = fp.ensure_mut();
let (writer, result) = getdelim_inner(buffer, *n, delim, fp);
match writer.buffer {
Some(buffer) => *lineptr = buffer.as_ptr(),
None => *lineptr = null_mut(),
}
*n = writer.capacity;
result?;
assert_ne!(writer.position, 0);
CIsizeResult::success(writer.position - 1)
}
#[no_mangle]
unsafe extern "C" fn getline(
lineptr: *mut *mut c_char,
size: *mut usize,
fp: *mut FILE,
) -> CIsizeResult {
getdelim(lineptr, size, b'\n' as _, fp)
}
#[no_mangle]
unsafe extern "C" fn gets(_buf: *mut c_char) -> *mut c_char {
unimplemented!("DO NOT USE")
}