240 lines
4.7 KiB
Rust
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)
|
|
}
|