296 lines
6.9 KiB
Rust
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")
|
|
}
|