Files
ygglibc/src/header/stdio/get_put.rs
T

240 lines
4.7 KiB
Rust

use core::{
ffi::{c_char, c_int, CStr},
ptr::null_mut,
};
use crate::{
error,
header::{stdlib::realloc, string::strlen},
io::{Read, Write},
};
use super::{
stderr, stdin, stdout,
unlocked::{flockfile, fputc_unlocked, fputs_unlocked, funlockfile, fwrite_unlocked},
EOF, FILE,
};
#[no_mangle]
unsafe extern "C" fn perror(msg: *const c_char) {
flockfile(stderr);
if !msg.is_null() {
fputs_unlocked(msg, stderr);
fputc_unlocked(b':' as _, stderr);
fputc_unlocked(b' ' as _, stderr);
}
fputs_unlocked(error::errno.to_c_str(), stderr);
fputc_unlocked(b'\n' as _, stderr);
funlockfile(stderr);
}
// Chars
#[no_mangle]
unsafe extern "C" fn fputc(c: c_int, stream: *mut FILE) -> c_int {
let stream = stream.as_mut().unwrap();
let c = c as u8;
match stream.write(&[c]) {
Ok(_) => c as c_int,
Err(_) => EOF,
}
}
#[no_mangle]
unsafe extern "C" fn putc(c: c_int, stream: *mut FILE) -> c_int {
fputc(c, stream)
}
#[no_mangle]
unsafe extern "C" fn putchar(c: c_int) -> c_int {
fputc(c, stdout)
}
// Strings
#[no_mangle]
unsafe extern "C" fn puts(s: *const c_char) -> c_int {
let r = fputs(s, stdout);
if r < 0 {
return r;
}
fputc(b'\n' as _, stdout)
}
#[no_mangle]
unsafe extern "C" fn fputs(s: *const c_char, stream: *mut FILE) -> c_int {
let stream = stream.as_mut().unwrap();
if s.is_null() {
panic!();
}
let s = CStr::from_ptr(s);
match stream.write(s.to_bytes()) {
Ok(_) => 0,
Err(_) => EOF,
}
}
#[no_mangle]
unsafe extern "C" fn fgetc(stream: *mut FILE) -> c_int {
let stream = stream.as_mut().unwrap();
let mut buf = [0];
match stream.read(&mut buf) {
Ok(1) => buf[0] as c_int,
Ok(_) => EOF,
Err(_) => EOF,
}
}
#[no_mangle]
unsafe extern "C" fn getc(stream: *mut FILE) -> c_int {
fgetc(stream)
}
#[no_mangle]
unsafe extern "C" fn getchar() -> c_int {
fgetc(stdin)
}
#[no_mangle]
unsafe extern "C" fn fgets(s: *mut c_char, size: c_int, stream: *mut FILE) -> *mut c_char {
let stream = stream.as_mut().unwrap();
if size <= 0 {
return null_mut();
}
// Nothing to read
if size == 1 {
*s = 0;
return s;
}
let size = size as usize;
let mut pos = 0;
let mut buf = [0];
while pos < size - 1 {
let ch = match stream.read(&mut buf) {
Ok(1) => buf[0],
Ok(_) => {
break;
}
Err(err) => {
return null_mut();
}
};
*s.add(pos) = ch as _;
pos += 1;
if ch == b'\n' {
break;
}
}
if pos == 0 {
null_mut()
} else {
*s.add(pos) = 0;
s
}
}
#[no_mangle]
unsafe extern "C" fn ungetc(c: c_int, stream: *mut FILE) -> c_int {
let stream = stream.as_mut().unwrap();
if stream.ungetc(c as _) {
c
} else {
EOF
}
}
// Line
struct MallocBufferWriter {
buffer: *mut c_char,
position: usize,
capacity: usize,
}
impl MallocBufferWriter {
fn putc(&mut self, ch: c_int) -> bool {
if self.position == self.capacity {
// Extend the buffer
self.capacity = (self.capacity + 64) & !63;
self.buffer = unsafe { realloc(self.buffer as _, self.capacity) as _ };
if self.buffer.is_null() {
// ENOMEM is set
return false;
}
}
unsafe {
*self.buffer.add(self.position) = ch as _;
}
self.position += 1;
true
}
}
#[no_mangle]
unsafe extern "C" fn getdelim(
lineptr: *mut *mut c_char,
n: *mut usize,
delim: c_int,
stream: *mut FILE,
) -> isize {
let lineptr = lineptr.as_mut().unwrap();
let n = n.as_mut().unwrap();
let mut capacity = *n;
if lineptr.is_null() {
capacity = 0;
}
let mut writer = MallocBufferWriter {
buffer: *lineptr,
position: 0,
capacity,
};
loop {
let ch = fgetc(stream);
if ch == EOF {
if writer.position == 0 {
// EOF and no data read
return -1;
}
break;
}
if !writer.putc(ch) {
return -1;
}
if ch == delim {
break;
}
}
if !writer.putc(0) {
return -1;
}
*lineptr = writer.buffer;
*n = writer.capacity;
// Minus the '\0'
(writer.position - 1).try_into().unwrap()
}
#[no_mangle]
unsafe extern "C" fn getline(lineptr: *mut *mut c_char, n: *mut usize, stream: *mut FILE) -> isize {
getdelim(lineptr, n, b'\n' as _, stream)
}