0.3.0: converge relibc to upstream 0.6.0 + Red Bear patches
This commit is contained in:
@@ -1,6 +1,25 @@
|
||||
sys_includes = ["stdarg.h", "stddef.h", "stdint.h", "sys/types.h"]
|
||||
sys_includes = ["stdarg.h", "stddef.h", "stdint.h", "sys/types.h", "features.h"]
|
||||
include_guard = "_RELIBC_STDIO_H"
|
||||
trailer = "#include <bits/stdio.h>"
|
||||
trailer = """
|
||||
#ifndef _RELIBC_BITS_STDIO_H
|
||||
#define _RELIBC_BITS_STDIO_H
|
||||
|
||||
// XXX: this is only here because cbindgen can't handle string constants
|
||||
#define P_tmpdir "/tmp"
|
||||
|
||||
// TODO: figure out why this duplicate is needed to compile libiconv
|
||||
typedef struct FILE FILE;
|
||||
|
||||
// A typedef doesn't suffice, because libgmp uses this definition to check if
|
||||
// STDIO was loaded.
|
||||
#define FILE FILE
|
||||
// Likewise, stdin, stdout, and stderr are expected to be macros.
|
||||
#define stdin stdin
|
||||
#define stdout stdout
|
||||
#define stderr stderr
|
||||
|
||||
#endif // _RELIBC_BITS_STDIO_H
|
||||
"""
|
||||
language = "C"
|
||||
style = "Type"
|
||||
no_includes = true
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::platform::types::*;
|
||||
use crate::platform::types::{c_int, c_uint, int32_t, off_t};
|
||||
|
||||
pub const EOF: c_int = -1;
|
||||
pub const BUFSIZ: c_int = 1024;
|
||||
@@ -24,13 +24,26 @@ pub const _IOFBF: c_int = 0;
|
||||
pub const _IOLBF: c_int = 1;
|
||||
pub const _IONBF: c_int = 2;
|
||||
|
||||
// renameat2
|
||||
// (uint instead of int is intentional)
|
||||
/// Rename but don't replace the target if it exists.
|
||||
pub const RENAME_NOREPLACE: c_uint = 0x01;
|
||||
/// Atomically swap two files.
|
||||
#[cfg(target_os = "linux")]
|
||||
pub const RENAME_EXCHANGE: c_uint = 0x02;
|
||||
#[cfg(target_os = "linux")]
|
||||
pub const RENAME_WHITEOUT: c_uint = 0x04;
|
||||
|
||||
// /dev/tty + nul
|
||||
pub const L_ctermid: usize = 9;
|
||||
// form of name is /XXXXXX, so 7
|
||||
pub const L_tmpnam: c_int = 7;
|
||||
// 36^6 (26 letters + 10 digits) is larger than i32::MAX, so just set to that
|
||||
// for now
|
||||
pub const TMP_MAX: int32_t = 2_147_483_647;
|
||||
// XXX: defined manually in bits/stdio.h as well because cbindgen can't handle
|
||||
// XXX: defined manually in cbindgen as well because it can't handle
|
||||
// string constants in any form AFAICT
|
||||
/// cbindgen:ignore
|
||||
pub const P_tmpdir: &[u8; 5] = b"/tmp\0";
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
|
||||
+26
-16
@@ -1,16 +1,22 @@
|
||||
use super::{constants, Buffer, BUFSIZ, FILE};
|
||||
use super::{BUFSIZ, Buffer, FILE, constants};
|
||||
use core::{cell::UnsafeCell, ptr};
|
||||
|
||||
use crate::{fs::File, io::LineWriter, platform::types::*, sync::Mutex};
|
||||
use alloc::vec::Vec;
|
||||
use crate::{fs::File, header::pthread, io::LineWriter, platform::types::c_int, sync::Once};
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
|
||||
// TODO: Change FILE to allow const fn initialization?
|
||||
pub struct GlobalFile(UnsafeCell<FILE>);
|
||||
|
||||
impl GlobalFile {
|
||||
fn new(file: c_int, flags: c_int) -> Self {
|
||||
let file = File::new(file);
|
||||
let writer = LineWriter::new(unsafe { file.get_ref() });
|
||||
let writer = Box::new(LineWriter::new(unsafe { file.get_ref() }));
|
||||
let mutex_attr = pthread::RlctMutexAttr {
|
||||
ty: pthread::PTHREAD_MUTEX_RECURSIVE,
|
||||
..Default::default()
|
||||
};
|
||||
GlobalFile(UnsafeCell::new(FILE {
|
||||
lock: Mutex::new(()),
|
||||
lock: pthread::RlctMutex::new(&mutex_attr).unwrap(),
|
||||
|
||||
file,
|
||||
flags: constants::F_PERM | flags,
|
||||
@@ -32,20 +38,24 @@ impl GlobalFile {
|
||||
// statics need to be Sync
|
||||
unsafe impl Sync for GlobalFile {}
|
||||
|
||||
lazy_static! {
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub static ref default_stdin: GlobalFile = GlobalFile::new(0, constants::F_NOWR);
|
||||
// TODO: Allow const fn initialization of FILE
|
||||
static DEFAULT_STDIN: Once<GlobalFile> = Once::new();
|
||||
static DEFAULT_STDOUT: Once<GlobalFile> = Once::new();
|
||||
static DEFAULT_STDERR: Once<GlobalFile> = Once::new();
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub static ref default_stdout: GlobalFile = GlobalFile::new(1, constants::F_NORD);
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub static ref default_stderr: GlobalFile = GlobalFile::new(2, constants::F_NORD);
|
||||
pub fn default_stdin() -> &'static GlobalFile {
|
||||
DEFAULT_STDIN.call_once(|| GlobalFile::new(0, constants::F_NOWR))
|
||||
}
|
||||
pub fn default_stdout() -> &'static GlobalFile {
|
||||
DEFAULT_STDOUT.call_once(|| GlobalFile::new(1, constants::F_NORD))
|
||||
}
|
||||
pub fn default_stderr() -> &'static GlobalFile {
|
||||
DEFAULT_STDERR.call_once(|| GlobalFile::new(2, constants::F_NORD))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub static mut stdin: *mut FILE = ptr::null_mut();
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub static mut stdout: *mut FILE = ptr::null_mut();
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub static mut stderr: *mut FILE = ptr::null_mut();
|
||||
|
||||
+26
-12
@@ -1,41 +1,55 @@
|
||||
use crate::{
|
||||
header::stdio::{FILE, F_NORD, F_NOWR},
|
||||
platform::types::*,
|
||||
header::stdio::{F_ERR, F_NORD, F_NOWR, FILE},
|
||||
platform::types::{c_int, size_t},
|
||||
};
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn __freadahead(stream: *mut FILE) -> size_t {
|
||||
let stream = unsafe { &mut *stream }.lock();
|
||||
|
||||
(stream.read_size.saturating_sub(stream.read_pos) + stream.unget.len()) as size_t
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn __fpending(stream: *mut FILE) -> size_t {
|
||||
let stream = unsafe { &mut *stream }.lock();
|
||||
|
||||
stream.writer.inner.buf.len() as size_t
|
||||
stream.writer.pending()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn __freadable(stream: *mut FILE) -> c_int {
|
||||
let stream = unsafe { &mut *stream }.lock();
|
||||
|
||||
(stream.flags & F_NORD == 0) as c_int
|
||||
c_int::from(stream.flags & F_NORD == 0)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn __fwritable(stream: *mut FILE) -> c_int {
|
||||
let stream = unsafe { &mut *stream }.lock();
|
||||
|
||||
(stream.flags & F_NOWR == 0) as c_int
|
||||
c_int::from(stream.flags & F_NOWR == 0)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn __fseterr(stream: *mut FILE) {
|
||||
let mut stream = unsafe { &mut *stream }.lock();
|
||||
|
||||
stream.flags |= F_ERR;
|
||||
}
|
||||
|
||||
//TODO: Check last operation when read-write
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn __freading(stream: *mut FILE) -> c_int {
|
||||
let stream = unsafe { &mut *stream }.lock();
|
||||
|
||||
(stream.flags & F_NORD == 0) as c_int
|
||||
c_int::from(stream.flags & F_NORD == 0)
|
||||
}
|
||||
|
||||
//TODO: Check last operation when read-write
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn __fwriting(stream: *mut FILE) -> c_int {
|
||||
let stream = unsafe { &mut *stream }.lock();
|
||||
|
||||
(stream.flags & F_NOWR == 0) as c_int
|
||||
c_int::from(stream.flags & F_NOWR == 0)
|
||||
}
|
||||
|
||||
+107
-17
@@ -1,31 +1,80 @@
|
||||
// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getline.html>.
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use core::ptr;
|
||||
use core::{intrinsics::unlikely, ptr};
|
||||
|
||||
use crate::{
|
||||
header::{stdio::FILE, stdlib},
|
||||
header::{
|
||||
errno::{EINVAL, ENOMEM, EOVERFLOW},
|
||||
stdio::FILE,
|
||||
stdlib,
|
||||
},
|
||||
io::BufRead,
|
||||
platform::types::*,
|
||||
platform::types::{c_char, c_int, c_void, size_t, ssize_t},
|
||||
};
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn __getline(
|
||||
use crate::{
|
||||
header::stdio::{F_EOF, F_ERR, feof, ferror},
|
||||
platform::ERRNO,
|
||||
};
|
||||
|
||||
/// see getdelim (getline is a special case of getdelim with delim == '\n')
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn getline(
|
||||
lineptr: *mut *mut c_char,
|
||||
n: *mut size_t,
|
||||
stream: *mut FILE,
|
||||
) -> ssize_t {
|
||||
__getdelim(lineptr, n, b'\n' as c_int, stream)
|
||||
unsafe { getdelim(lineptr, n, c_int::from(b'\n'), stream) }
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn __getdelim(
|
||||
// One *could* read the standard as 'getdelim sets the stream error flag on *any* error, though
|
||||
// since glibc doesn't seem to do this, I won't either
|
||||
|
||||
/// <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getline.html>
|
||||
///
|
||||
/// # Safety
|
||||
/// - `lineptr, *lineptr, `n`, `stream` pointers must be valid and have to be aligned.
|
||||
/// - `stream` has to be a valid file handle returned by fopen and likes.
|
||||
///
|
||||
/// # Deviation from POSIX
|
||||
/// - **EINVAL is set on stream being NULL or delim not fitting into char** (POSIX allows UB)
|
||||
/// - **`*n` can contain invalid data.** The buffer size `n` is not read, instead realloc is called each time. That is in principle
|
||||
/// inefficent since the buffer is reallocated in memory for every call, but if `n` is by mistake
|
||||
/// bigger than the number of bytes allocated for the buffer, there can be no out-of-bounds write.
|
||||
/// - On non-stream-related errors, the error indicator of the stream is *not* set. Posix states
|
||||
/// "If an error occurs, the error indicator for the stream shall be set, and the function shall
|
||||
/// return -1 and set errno to indicate the error." but in cases that produce EINVAL even glibc
|
||||
/// doesn't seem to set the error indicator, so we also don't.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn getdelim(
|
||||
lineptr: *mut *mut c_char,
|
||||
n: *mut size_t,
|
||||
delim: c_int,
|
||||
stream: *mut FILE,
|
||||
) -> ssize_t {
|
||||
let lineptr = &mut *lineptr;
|
||||
let n = &mut *n;
|
||||
let delim = delim as u8;
|
||||
let (lineptr, n, stream) = if let (Some(ptr), Some(n), Some(file)) =
|
||||
(unsafe { lineptr.as_mut() }, unsafe { n.as_mut() }, unsafe {
|
||||
stream.as_mut()
|
||||
}) {
|
||||
(ptr, n, file)
|
||||
} else {
|
||||
ERRNO.set(EINVAL);
|
||||
return -1 as ssize_t;
|
||||
};
|
||||
|
||||
if unsafe { feof(stream) } != 0 || unsafe { ferror(stream) } != 0 {
|
||||
return -1 as ssize_t;
|
||||
}
|
||||
|
||||
// POSIX specifies UB but we test anyway
|
||||
// returning EINVAL in that case
|
||||
let delim: u8 = if let Ok(delim) = delim.try_into() {
|
||||
delim
|
||||
} else {
|
||||
ERRNO.set(EINVAL);
|
||||
return -1;
|
||||
};
|
||||
|
||||
//TODO: More efficient algorithm using lineptr and n instead of this vec
|
||||
let mut buf = Vec::new();
|
||||
@@ -33,23 +82,64 @@ pub unsafe extern "C" fn __getdelim(
|
||||
let mut stream = (*stream).lock();
|
||||
match stream.read_until(delim, &mut buf) {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => return -1,
|
||||
Err(err) => {
|
||||
stream.flags &= F_ERR;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// "[EOVERFLOW]
|
||||
// The number of bytes to be written into the buffer, including the delimiter character (if encountered), would exceed {SSIZE_MAX}."
|
||||
if unlikely(count > ssize_t::MAX as usize) {
|
||||
ERRNO.set(EOVERFLOW);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// we reached EOF if either
|
||||
// - we have no last elem (because vec is empty), or
|
||||
// - the last elem doesn't match the delimiter
|
||||
let eof_reached = if let Some(last) = buf.last() {
|
||||
*last == delim
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
// "If the end-of-file indicator for the stream is set, or if no characters were read and the
|
||||
// stream is at end-of-file, the end-of-file indicator for the stream shall be set and the
|
||||
// function shall return -1."
|
||||
if eof_reached {
|
||||
stream.flags &= F_EOF;
|
||||
if count == 0 {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Check errors and improve safety
|
||||
{
|
||||
// Allocate lineptr to size of buf and set n to size of lineptr
|
||||
// Allocate lineptr to size of buf plus NUL byte and set n to size of lineptr
|
||||
*n = count + 1;
|
||||
*lineptr = stdlib::realloc(*lineptr as *mut c_void, *n) as *mut c_char;
|
||||
// The advantage in always realloc'ing is that even if the user supplies a wrong n, this
|
||||
// doesn't break
|
||||
*lineptr = unsafe { stdlib::realloc((*lineptr).cast::<c_void>(), *n) }.cast::<c_char>();
|
||||
if unlikely(lineptr.is_null() && *n != 0usize) {
|
||||
// memory error; realloc returns NULL on alloc'ing 0 bytes
|
||||
ERRNO.set(ENOMEM);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Copy buf to lineptr
|
||||
ptr::copy(buf.as_ptr(), *lineptr as *mut u8, count);
|
||||
unsafe { ptr::copy(buf.as_ptr(), (*lineptr).cast::<u8>(), count) };
|
||||
|
||||
// NUL terminate lineptr
|
||||
*lineptr.offset(count as isize) = 0;
|
||||
unsafe { *lineptr.add(count) = 0 };
|
||||
|
||||
// TODO remove
|
||||
/*eprintln!(
|
||||
"[DBG]{}: {}, {:?}, {:?}, {:?}", line!(),
|
||||
String::from_utf8(buf).unwrap(), count, *n, *lineptr
|
||||
);*/
|
||||
// Return allocated size
|
||||
*n as ssize_t
|
||||
count as ssize_t
|
||||
}
|
||||
}
|
||||
|
||||
+44
-29
@@ -1,36 +1,47 @@
|
||||
use alloc::boxed::Box;
|
||||
|
||||
use super::{constants::*, Buffer, FILE};
|
||||
use super::{
|
||||
Buffer, FILE,
|
||||
constants::{BUFSIZ, F_APP, F_NORD, F_NOWR},
|
||||
};
|
||||
use crate::{
|
||||
c_str::CStr,
|
||||
error::Errno,
|
||||
fs::File,
|
||||
header::{errno, fcntl::*, string::strchr},
|
||||
io::LineWriter,
|
||||
platform::{self, types::*},
|
||||
sync::Mutex,
|
||||
header::{
|
||||
errno::EINVAL,
|
||||
fcntl::{
|
||||
F_GETFL, F_SETFD, F_SETFL, FD_CLOEXEC, O_APPEND, O_CLOEXEC, O_CREAT, O_EXCL, O_RDONLY,
|
||||
O_RDWR, O_TRUNC, O_WRONLY, fcntl,
|
||||
},
|
||||
pthread,
|
||||
},
|
||||
io::BufWriter,
|
||||
platform::types::{c_int, c_ulonglong},
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
/// Parse mode flags as a string and output a mode flags integer
|
||||
pub unsafe fn parse_mode_flags(mode_str: *const c_char) -> i32 {
|
||||
let mut flags = if !strchr(mode_str, b'+' as i32).is_null() {
|
||||
pub fn parse_mode_flags(mode_str: CStr) -> i32 {
|
||||
let mut flags = if mode_str.contains(b'+') {
|
||||
O_RDWR
|
||||
} else if (*mode_str) == b'r' as i8 {
|
||||
} else if mode_str.first() == b'r' {
|
||||
O_RDONLY
|
||||
} else {
|
||||
O_WRONLY
|
||||
};
|
||||
if !strchr(mode_str, b'x' as i32).is_null() {
|
||||
if mode_str.contains(b'x') {
|
||||
flags |= O_EXCL;
|
||||
}
|
||||
if !strchr(mode_str, b'e' as i32).is_null() {
|
||||
if mode_str.contains(b'e') {
|
||||
flags |= O_CLOEXEC;
|
||||
}
|
||||
if (*mode_str) != b'r' as i8 {
|
||||
if mode_str.first() != b'r' {
|
||||
flags |= O_CREAT;
|
||||
}
|
||||
if (*mode_str) == b'w' as i8 {
|
||||
if mode_str.first() == b'w' {
|
||||
flags |= O_TRUNC;
|
||||
} else if (*mode_str) == b'a' as i8 {
|
||||
} else if mode_str.first() == b'a' {
|
||||
flags |= O_APPEND;
|
||||
}
|
||||
|
||||
@@ -38,34 +49,38 @@ pub unsafe fn parse_mode_flags(mode_str: *const c_char) -> i32 {
|
||||
}
|
||||
|
||||
/// Open a file with the file descriptor `fd` in the mode `mode`
|
||||
pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option<*mut FILE> {
|
||||
if *mode != b'r' as i8 && *mode != b'w' as i8 && *mode != b'a' as i8 {
|
||||
platform::errno = errno::EINVAL;
|
||||
return None;
|
||||
pub fn _fdopen(fd: c_int, mode: CStr) -> Result<Box<FILE>, Errno> {
|
||||
if mode.first() != b'r' && mode.first() != b'w' && mode.first() != b'a' {
|
||||
return Err(Errno(EINVAL));
|
||||
}
|
||||
|
||||
let mut flags = 0;
|
||||
if strchr(mode, b'+' as i32).is_null() {
|
||||
flags |= if *mode == b'r' as i8 { F_NOWR } else { F_NORD };
|
||||
if !mode.contains(b'+') {
|
||||
flags |= if mode.first() == b'r' { F_NOWR } else { F_NORD };
|
||||
}
|
||||
|
||||
if !strchr(mode, b'e' as i32).is_null() {
|
||||
sys_fcntl(fd, F_SETFD, FD_CLOEXEC);
|
||||
if mode.contains(b'e') {
|
||||
unsafe {
|
||||
fcntl(fd, F_SETFD, FD_CLOEXEC as c_ulonglong);
|
||||
}
|
||||
}
|
||||
|
||||
if *mode == 'a' as i8 {
|
||||
let f = sys_fcntl(fd, F_GETFL, 0);
|
||||
if mode.first() == b'a' {
|
||||
let f = unsafe { fcntl(fd, F_GETFL, 0) };
|
||||
if (f & O_APPEND) == 0 {
|
||||
sys_fcntl(fd, F_SETFL, f | O_APPEND);
|
||||
unsafe { fcntl(fd, F_SETFL, (f | O_APPEND) as c_ulonglong) };
|
||||
}
|
||||
flags |= F_APP;
|
||||
}
|
||||
|
||||
let file = File::new(fd);
|
||||
let writer = LineWriter::new(file.get_ref());
|
||||
|
||||
Some(Box::into_raw(Box::new(FILE {
|
||||
lock: Mutex::new(()),
|
||||
let writer = Box::new(BufWriter::new(unsafe { file.get_ref() }));
|
||||
let mutex_attr = pthread::RlctMutexAttr {
|
||||
ty: pthread::PTHREAD_MUTEX_RECURSIVE,
|
||||
..Default::default()
|
||||
};
|
||||
Ok(Box::new(FILE {
|
||||
lock: pthread::RlctMutex::new(&mutex_attr).unwrap(),
|
||||
|
||||
file,
|
||||
flags,
|
||||
@@ -78,5 +93,5 @@ pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option<*mut FILE> {
|
||||
pid: None,
|
||||
|
||||
orientation: 0,
|
||||
})))
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
use super::{fseek_locked, ftell_locked, FILE, SEEK_SET};
|
||||
use crate::core_io::Read;
|
||||
struct LookAheadBuffer {
|
||||
buf: *const u8,
|
||||
pos: isize,
|
||||
look_ahead: isize,
|
||||
}
|
||||
impl LookAheadBuffer {
|
||||
fn look_ahead(&mut self) -> Result<Option<u8>, i32> {
|
||||
let byte = unsafe { *self.buf.offset(self.look_ahead) };
|
||||
if byte == 0 {
|
||||
Ok(None)
|
||||
} else {
|
||||
self.look_ahead += 1;
|
||||
Ok(Some(byte))
|
||||
}
|
||||
}
|
||||
|
||||
fn commit(&mut self) {
|
||||
self.pos = self.look_ahead;
|
||||
}
|
||||
}
|
||||
|
||||
impl From<*const u8> for LookAheadBuffer {
|
||||
fn from(buff: *const u8) -> LookAheadBuffer {
|
||||
LookAheadBuffer {
|
||||
buf: buff,
|
||||
pos: 0,
|
||||
look_ahead: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LookAheadFile<'a> {
|
||||
f: &'a mut FILE,
|
||||
look_ahead: i64,
|
||||
}
|
||||
|
||||
impl<'a> LookAheadFile<'a> {
|
||||
fn look_ahead(&mut self) -> Result<Option<u8>, i32> {
|
||||
let buf = &mut [0];
|
||||
let seek = unsafe { ftell_locked(self.f) };
|
||||
unsafe { fseek_locked(self.f, self.look_ahead, SEEK_SET) };
|
||||
let ret = match self.f.read(buf) {
|
||||
Ok(0) => Ok(None),
|
||||
Ok(_) => Ok(Some(buf[0])),
|
||||
Err(_) => Err(-1),
|
||||
};
|
||||
unsafe { fseek_locked(self.f, seek, SEEK_SET) };
|
||||
self.look_ahead += 1;
|
||||
ret
|
||||
}
|
||||
|
||||
fn commit(&mut self) {
|
||||
unsafe { fseek_locked(self.f, self.look_ahead, SEEK_SET) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a mut FILE> for LookAheadFile<'a> {
|
||||
fn from(f: &'a mut FILE) -> LookAheadFile<'a> {
|
||||
let look_ahead = unsafe { ftell_locked(f) } as i64;
|
||||
LookAheadFile { f, look_ahead }
|
||||
}
|
||||
}
|
||||
|
||||
enum LookAheadReaderEnum<'a> {
|
||||
FILE(LookAheadFile<'a>),
|
||||
// (buffer, location)
|
||||
BUFFER(LookAheadBuffer),
|
||||
}
|
||||
|
||||
pub struct LookAheadReader<'a>(LookAheadReaderEnum<'a>);
|
||||
|
||||
impl<'a> LookAheadReader<'a> {
|
||||
pub fn lookahead1(&mut self) -> Result<Option<u8>, i32> {
|
||||
match &mut self.0 {
|
||||
LookAheadReaderEnum::FILE(f) => f.look_ahead(),
|
||||
LookAheadReaderEnum::BUFFER(b) => b.look_ahead(),
|
||||
}
|
||||
}
|
||||
pub fn commit(&mut self) {
|
||||
match &mut self.0 {
|
||||
LookAheadReaderEnum::FILE(f) => f.commit(),
|
||||
LookAheadReaderEnum::BUFFER(b) => b.commit(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a mut FILE> for LookAheadReader<'a> {
|
||||
fn from(f: &'a mut FILE) -> LookAheadReader {
|
||||
LookAheadReader(LookAheadReaderEnum::FILE(f.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<*const u8> for LookAheadReader<'a> {
|
||||
fn from(buff: *const u8) -> LookAheadReader<'a> {
|
||||
LookAheadReader(LookAheadReaderEnum::BUFFER(buff.into()))
|
||||
}
|
||||
}
|
||||
+779
-319
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,124 @@
|
||||
use alloc::{boxed::Box, vec, vec::Vec};
|
||||
use core::ptr;
|
||||
|
||||
use super::{
|
||||
Buffer, FILE,
|
||||
constants::{BUFSIZ, F_NORD},
|
||||
};
|
||||
use crate::{
|
||||
error::{Errno, ResultExtPtrMut},
|
||||
fs::File,
|
||||
header::{
|
||||
errno::{EFAULT, ENOMEM},
|
||||
fcntl, pthread, stdlib, unistd,
|
||||
},
|
||||
io::{self, BufWriter, Write},
|
||||
platform::{
|
||||
ERRNO,
|
||||
types::{c_char, size_t},
|
||||
},
|
||||
};
|
||||
|
||||
struct MemstreamWriter {
|
||||
bufp: *mut *mut c_char,
|
||||
sizep: *mut size_t,
|
||||
current: *mut c_char,
|
||||
buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
unsafe impl Send for MemstreamWriter {}
|
||||
|
||||
impl MemstreamWriter {
|
||||
fn new(bufp: *mut *mut c_char, sizep: *mut size_t) -> Self {
|
||||
Self {
|
||||
bufp,
|
||||
sizep,
|
||||
current: ptr::null_mut(),
|
||||
buffer: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn sync_output(&mut self) -> io::Result<()> {
|
||||
let size = self.buffer.len();
|
||||
let alloc_size = size
|
||||
.checked_add(1)
|
||||
.ok_or_else(|| io::Error::from_raw_os_error(ENOMEM))?;
|
||||
|
||||
let raw = if self.current.is_null() {
|
||||
unsafe { stdlib::malloc(alloc_size) }
|
||||
} else {
|
||||
unsafe { stdlib::realloc(self.current.cast(), alloc_size) }
|
||||
};
|
||||
if raw.is_null() {
|
||||
return Err(io::Error::from_raw_os_error(ENOMEM));
|
||||
}
|
||||
|
||||
let raw = raw.cast::<c_char>();
|
||||
if size != 0 {
|
||||
unsafe { ptr::copy_nonoverlapping(self.buffer.as_ptr(), raw.cast::<u8>(), size) };
|
||||
}
|
||||
unsafe {
|
||||
*raw.add(size) = 0;
|
||||
*self.bufp = raw;
|
||||
*self.sizep = size;
|
||||
}
|
||||
self.current = raw;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for MemstreamWriter {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.buffer
|
||||
.try_reserve(buf.len())
|
||||
.map_err(|_| io::Error::from_raw_os_error(ENOMEM))?;
|
||||
self.buffer.extend_from_slice(buf);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.sync_output()
|
||||
}
|
||||
}
|
||||
|
||||
fn create_memstream(bufp: *mut *mut c_char, sizep: *mut size_t) -> Result<Box<FILE>, Errno> {
|
||||
if bufp.is_null() || sizep.is_null() {
|
||||
return Err(Errno(EFAULT));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
*bufp = ptr::null_mut();
|
||||
*sizep = 0;
|
||||
}
|
||||
|
||||
let mut fds = [0; 2];
|
||||
if unsafe { unistd::pipe2(fds.as_mut_ptr(), fcntl::O_CLOEXEC) } != 0 {
|
||||
return Err(Errno(ERRNO.get()));
|
||||
}
|
||||
let _ = unistd::close(fds[0]);
|
||||
|
||||
let file = File::new(fds[1]);
|
||||
let writer = Box::new(BufWriter::new(MemstreamWriter::new(bufp, sizep)));
|
||||
let mutex_attr = pthread::RlctMutexAttr {
|
||||
ty: pthread::PTHREAD_MUTEX_RECURSIVE,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Ok(Box::new(FILE {
|
||||
lock: pthread::RlctMutex::new(&mutex_attr).unwrap(),
|
||||
file,
|
||||
flags: F_NORD,
|
||||
read_buf: Buffer::Owned(vec![0; BUFSIZ as usize]),
|
||||
read_pos: 0,
|
||||
read_size: 0,
|
||||
unget: Vec::new(),
|
||||
writer,
|
||||
pid: None,
|
||||
orientation: 0,
|
||||
}))
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn open_memstream(bufp: *mut *mut c_char, sizep: *mut size_t) -> *mut FILE {
|
||||
create_memstream(bufp, sizep).or_errno_null_mut()
|
||||
}
|
||||
+765
-304
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,180 @@
|
||||
use super::{FILE, SEEK_SET, fseek_locked, ftell_locked};
|
||||
use crate::{
|
||||
c_str::{CStr, Kind, NulStr, Thin, WStr, Wide},
|
||||
header::{
|
||||
errno::EILSEQ,
|
||||
stdlib::MB_CUR_MAX,
|
||||
wchar::{get_char_encoded_length, mbrtowc},
|
||||
wctype::WEOF,
|
||||
},
|
||||
io::Read,
|
||||
platform::{
|
||||
ERRNO,
|
||||
types::{c_char, off_t, wchar_t, wint_t},
|
||||
},
|
||||
};
|
||||
use core::{
|
||||
iter::Iterator,
|
||||
marker::PhantomData,
|
||||
ptr::{self},
|
||||
};
|
||||
|
||||
pub struct BufferReader<'a, T: Kind> {
|
||||
buf: NulStr<'a, T>,
|
||||
}
|
||||
|
||||
impl<'a> From<WStr<'a>> for BufferReader<'a, Wide> {
|
||||
fn from(buff: WStr<'a>) -> Self {
|
||||
BufferReader { buf: buff }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<CStr<'a>> for BufferReader<'a, Thin> {
|
||||
fn from(buff: CStr<'a>) -> Self {
|
||||
BufferReader { buf: buff }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Kind> Iterator for BufferReader<'a, T> {
|
||||
type Item = Result<T::Char, i32>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.buf.split_first().map(|(c, r)| {
|
||||
self.buf = r;
|
||||
Ok(c)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FileReader<'a, T: Kind> {
|
||||
f: &'a mut FILE,
|
||||
position: off_t,
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: Kind> FileReader<'a, T> {
|
||||
// Gets the wchar at the current position
|
||||
#[inline]
|
||||
fn get_curret_char(&mut self) -> Result<Option<(T::Char, usize)>, i32> {
|
||||
if T::IS_THIN_NOT_WIDE {
|
||||
let mut buf: [u8; 1] = [0];
|
||||
match self.f.read(&mut buf) {
|
||||
Ok(0) => Ok(None),
|
||||
Ok(n) => Ok(Some((T::Char::from(buf[0]), n))),
|
||||
Err(_) => Err(-1),
|
||||
}
|
||||
} else {
|
||||
let buf = &mut [0; MB_CUR_MAX as usize];
|
||||
let mut encoded_length = 0;
|
||||
let mut bytes_read = 0;
|
||||
|
||||
loop {
|
||||
match self.f.read(&mut buf[bytes_read..bytes_read + 1]) {
|
||||
Ok(0) => return Ok(None),
|
||||
Ok(_) => {}
|
||||
Err(_) => return Err(-1),
|
||||
}
|
||||
|
||||
bytes_read += 1;
|
||||
|
||||
if bytes_read == 1 {
|
||||
encoded_length = if let Some(el) = get_char_encoded_length(buf[0]) {
|
||||
el
|
||||
} else {
|
||||
ERRNO.set(EILSEQ);
|
||||
return Self::get_char_from_wint(WEOF).map(|c| Some((c, 0)));
|
||||
};
|
||||
}
|
||||
|
||||
if bytes_read >= encoded_length {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let mut wc: wchar_t = 0;
|
||||
unsafe {
|
||||
mbrtowc(
|
||||
&raw mut wc,
|
||||
buf.as_ptr().cast::<c_char>(),
|
||||
encoded_length,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
}
|
||||
|
||||
Self::get_char_from_wint(wc as wint_t).map(|c| Some((c, encoded_length)))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_char_from_wint(wc: wint_t) -> Result<T::Char, i32> {
|
||||
if let Some(wc_char) = T::chars_from_bytes(&wc.to_be_bytes())
|
||||
&& wc_char.len() == 1
|
||||
{
|
||||
Ok(wc_char[0])
|
||||
} else {
|
||||
Err(-1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Kind> Iterator for FileReader<'a, T> {
|
||||
type Item = Result<T::Char, i32>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
unsafe { fseek_locked(self.f, self.position, SEEK_SET) };
|
||||
|
||||
match self.get_curret_char() {
|
||||
Ok(Some((wc, encoded_length))) => {
|
||||
unsafe { fseek_locked(self.f, self.position, SEEK_SET) };
|
||||
self.position += encoded_length as off_t;
|
||||
Some(Ok(wc))
|
||||
}
|
||||
Ok(None) => None,
|
||||
Err(e) => Some(Err(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Kind> From<&'a mut FILE> for FileReader<'a, T> {
|
||||
fn from(f: &'a mut FILE) -> Self {
|
||||
let position = unsafe { ftell_locked(f) } as off_t;
|
||||
FileReader {
|
||||
f,
|
||||
position,
|
||||
phantom: PhantomData::<T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Reader<'a, T: Kind> {
|
||||
FILE(FileReader<'a, T>, PhantomData<T>),
|
||||
BUFFER(BufferReader<'a, T>),
|
||||
}
|
||||
|
||||
impl<'a, T: Kind> Iterator for Reader<'a, T> {
|
||||
type Item = Result<T::Char, i32>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self {
|
||||
Self::FILE(r, _) => r.next(),
|
||||
Self::BUFFER(r) => r.next(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Kind> From<&'a mut FILE> for Reader<'a, T> {
|
||||
fn from(f: &'a mut FILE) -> Self {
|
||||
Self::FILE(f.into(), PhantomData::<T>)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<WStr<'a>> for Reader<'a, Wide> {
|
||||
fn from(buff: WStr<'a>) -> Self {
|
||||
Self::BUFFER(buff.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<CStr<'a>> for Reader<'a, Thin> {
|
||||
fn from(buff: CStr<'a>) -> Self {
|
||||
Self::BUFFER(buff.into())
|
||||
}
|
||||
}
|
||||
+196
-145
@@ -1,51 +1,56 @@
|
||||
use super::lookaheadreader::LookAheadReader;
|
||||
use crate::platform::types::*;
|
||||
use super::reader::Reader;
|
||||
use crate::{
|
||||
c_str::Kind,
|
||||
header::stdio::printf::IntKind,
|
||||
platform::types::{
|
||||
c_char, c_double, c_float, c_int, c_long, c_longlong, c_short, c_uchar, c_uint, c_ulong,
|
||||
c_ulonglong, c_ushort, c_void, intmax_t, ptrdiff_t, size_t, ssize_t, uintmax_t, wchar_t,
|
||||
},
|
||||
};
|
||||
use alloc::{string::String, vec::Vec};
|
||||
use core::ffi::VaList as va_list;
|
||||
use core::{ffi::VaList as va_list, iter::Peekable};
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
enum IntKind {
|
||||
Byte,
|
||||
Short,
|
||||
Int,
|
||||
Long,
|
||||
LongLong,
|
||||
IntMax,
|
||||
PtrDiff,
|
||||
Size,
|
||||
enum CharKind {
|
||||
Ascii,
|
||||
Wide,
|
||||
}
|
||||
|
||||
/// Helper function for progressing a C string
|
||||
unsafe fn next_byte(string: &mut *const c_char) -> Result<u8, c_int> {
|
||||
let c = **string as u8;
|
||||
*string = string.offset(1);
|
||||
if c == 0 {
|
||||
Err(-1)
|
||||
fn next_char<T: Kind>(lar: &mut Peekable<Reader<'_, T>>) -> Result<char, c_int> {
|
||||
if let Some(c) = lar.next().transpose()? {
|
||||
char::try_from(c.into()).map_err(|_| -1)
|
||||
} else {
|
||||
Ok(c)
|
||||
Ok('\0')
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn inner_scanf(
|
||||
mut r: LookAheadReader,
|
||||
mut format: *const c_char,
|
||||
macro_rules! wc_as_char {
|
||||
($c:ident) => {
|
||||
char::try_from($c.into()).map_err(|_| -1)?
|
||||
};
|
||||
}
|
||||
|
||||
pub unsafe fn inner_scanf<T: Kind>(
|
||||
mut r: Reader<T>,
|
||||
format: Reader<T>,
|
||||
mut ap: va_list,
|
||||
) -> Result<c_int, c_int> {
|
||||
let mut matched = 0;
|
||||
let mut byte = 0;
|
||||
let mut character: char = '\0';
|
||||
let mut skip_read = false;
|
||||
let mut count = 0;
|
||||
let mut format = format.peekable();
|
||||
|
||||
macro_rules! read {
|
||||
() => {{
|
||||
match r.lookahead1() {
|
||||
Ok(None) => false,
|
||||
Ok(Some(b)) => {
|
||||
byte = b;
|
||||
match r.next() {
|
||||
None => false,
|
||||
Some(Ok(b)) => {
|
||||
character = wc_as_char!(b);
|
||||
count += 1;
|
||||
true
|
||||
}
|
||||
Err(x) => return Err(x),
|
||||
Some(Err(x)) => return Err(x),
|
||||
}
|
||||
}};
|
||||
}
|
||||
@@ -71,38 +76,37 @@ unsafe fn inner_scanf(
|
||||
}
|
||||
}
|
||||
|
||||
while *format != 0 {
|
||||
let mut c = next_byte(&mut format)?;
|
||||
while format.peek().is_some() {
|
||||
let mut c = next_char(&mut format)?;
|
||||
|
||||
if c == b' ' {
|
||||
if c == ' ' {
|
||||
maybe_read!(noreset);
|
||||
|
||||
while (byte as char).is_whitespace() {
|
||||
while (character).is_whitespace() {
|
||||
if !read!() {
|
||||
return Ok(matched);
|
||||
}
|
||||
}
|
||||
|
||||
skip_read = true;
|
||||
} else if c != b'%' {
|
||||
} else if c != '%' {
|
||||
maybe_read!();
|
||||
if c != byte {
|
||||
if c != character {
|
||||
return Ok(matched);
|
||||
}
|
||||
r.commit();
|
||||
} else {
|
||||
c = next_byte(&mut format)?;
|
||||
c = next_char(&mut format)?;
|
||||
|
||||
let mut ignore = false;
|
||||
if c == b'*' {
|
||||
if c == '*' {
|
||||
ignore = true;
|
||||
c = next_byte(&mut format)?;
|
||||
c = next_char(&mut format)?;
|
||||
}
|
||||
|
||||
let mut width = String::new();
|
||||
while c >= b'0' && c <= b'9' {
|
||||
width.push(c as char);
|
||||
c = next_byte(&mut format)?;
|
||||
while c.is_ascii_digit() {
|
||||
width.push(c);
|
||||
c = next_char(&mut format)?;
|
||||
}
|
||||
let mut width = if width.is_empty() {
|
||||
None
|
||||
@@ -118,65 +122,72 @@ unsafe fn inner_scanf(
|
||||
let mut eof = false;
|
||||
|
||||
let mut kind = IntKind::Int;
|
||||
let mut c_kind = CharKind::Ascii;
|
||||
loop {
|
||||
kind = match c {
|
||||
b'h' => {
|
||||
match c {
|
||||
'h' => {
|
||||
if kind == IntKind::Short || kind == IntKind::Byte {
|
||||
IntKind::Byte
|
||||
kind = IntKind::Byte;
|
||||
} else {
|
||||
IntKind::Short
|
||||
kind = IntKind::Short;
|
||||
}
|
||||
}
|
||||
b'j' => IntKind::IntMax,
|
||||
b'l' => {
|
||||
'j' => kind = IntKind::IntMax,
|
||||
'l' => {
|
||||
if kind == IntKind::Long || kind == IntKind::LongLong {
|
||||
IntKind::LongLong
|
||||
kind = IntKind::LongLong;
|
||||
} else {
|
||||
IntKind::Long
|
||||
kind = IntKind::Long;
|
||||
}
|
||||
}
|
||||
b'q' | b'L' => IntKind::LongLong,
|
||||
b't' => IntKind::PtrDiff,
|
||||
b'z' => IntKind::Size,
|
||||
'q' | 'L' => kind = IntKind::LongLong,
|
||||
't' => kind = IntKind::PtrDiff,
|
||||
'z' => kind = IntKind::Size,
|
||||
// If kind is Long, means we found a 'l' before finding 'c' or 's'. In this
|
||||
// case the format corresponds to a wide char/string
|
||||
'c' | 's' if kind == IntKind::Long && !T::IS_THIN_NOT_WIDE => {
|
||||
c_kind = CharKind::Wide;
|
||||
break;
|
||||
}
|
||||
_ => break,
|
||||
};
|
||||
}
|
||||
|
||||
c = next_byte(&mut format)?;
|
||||
c = next_char(&mut format)?;
|
||||
}
|
||||
|
||||
if c != b'n' {
|
||||
if c != 'n' {
|
||||
maybe_read!(noreset);
|
||||
}
|
||||
match c {
|
||||
b'%' => {
|
||||
while (byte as char).is_whitespace() {
|
||||
'%' => {
|
||||
while (character).is_whitespace() {
|
||||
if !read!() {
|
||||
return Ok(matched);
|
||||
}
|
||||
}
|
||||
|
||||
if byte != b'%' {
|
||||
if character != '%' {
|
||||
return Err(matched);
|
||||
} else if !read!() {
|
||||
return Ok(matched);
|
||||
}
|
||||
}
|
||||
b'd' | b'i' | b'o' | b'u' | b'x' | b'X' | b'f' | b'e' | b'g' | b'E' | b'a'
|
||||
| b'p' => {
|
||||
while (byte as char).is_whitespace() {
|
||||
|
||||
'd' | 'i' | 'o' | 'u' | 'x' | 'X' | 'f' | 'e' | 'g' | 'E' | 'a' | 'p' => {
|
||||
while character.is_whitespace() {
|
||||
if !read!() {
|
||||
return Ok(matched);
|
||||
}
|
||||
}
|
||||
|
||||
let pointer = c == b'p';
|
||||
let pointer = c == 'p';
|
||||
// Pointers aren't automatic, but we do want to parse "0x"
|
||||
let auto = c == b'i' || pointer;
|
||||
let float = c == b'f' || c == b'e' || c == b'g' || c == b'E' || c == b'a';
|
||||
let auto = c == 'i' || pointer;
|
||||
let float = c == 'f' || c == 'e' || c == 'g' || c == 'E' || c == 'a';
|
||||
|
||||
let mut radix = match c {
|
||||
b'o' => 8,
|
||||
b'x' | b'X' | b'p' => 16,
|
||||
'o' => 8,
|
||||
'x' | 'X' | 'p' => 16,
|
||||
_ => 10,
|
||||
};
|
||||
|
||||
@@ -184,16 +195,16 @@ unsafe fn inner_scanf(
|
||||
let mut dot = false;
|
||||
|
||||
while width.map(|w| w > 0).unwrap_or(true)
|
||||
&& ((byte >= b'0' && byte <= b'7')
|
||||
|| (radix >= 10 && (byte >= b'8' && byte <= b'9'))
|
||||
|| (float && !dot && byte == b'.')
|
||||
&& (('0'..='7').contains(&character)
|
||||
|| (radix >= 10 && ('8'..='9').contains(&character))
|
||||
|| (float && !dot && character == '.')
|
||||
|| (radix == 16
|
||||
&& ((byte >= b'a' && byte <= b'f')
|
||||
|| (byte >= b'A' && byte <= b'F'))))
|
||||
&& (('a'..='f').contains(&character)
|
||||
|| ('A'..='F').contains(&character))))
|
||||
{
|
||||
if auto
|
||||
&& n.is_empty()
|
||||
&& byte == b'0'
|
||||
&& character == '0'
|
||||
&& width.map(|w| w > 0).unwrap_or(true)
|
||||
{
|
||||
if !pointer {
|
||||
@@ -204,7 +215,7 @@ unsafe fn inner_scanf(
|
||||
return Ok(matched);
|
||||
}
|
||||
if width.map(|w| w > 0).unwrap_or(true)
|
||||
&& (byte == b'x' || byte == b'X')
|
||||
&& (character == 'x' || character == 'X')
|
||||
{
|
||||
radix = 16;
|
||||
width = width.map(|w| w - 1);
|
||||
@@ -214,15 +225,14 @@ unsafe fn inner_scanf(
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if byte == b'.' {
|
||||
if character == '.' {
|
||||
// Don't allow another dot
|
||||
dot = true;
|
||||
}
|
||||
n.push(byte as char);
|
||||
r.commit();
|
||||
n.push(character);
|
||||
width = width.map(|w| w - 1);
|
||||
if width.map(|w| w > 0).unwrap_or(true) && !read!() {
|
||||
return Ok(matched);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,18 +244,18 @@ unsafe fn inner_scanf(
|
||||
n.parse::<$type>().map_err(|_| 0)?
|
||||
};
|
||||
if !ignore {
|
||||
*ap.arg::<*mut $type>() = n;
|
||||
unsafe { *ap.next_arg::<*mut $type>() = n };
|
||||
matched += 1;
|
||||
}
|
||||
}};
|
||||
(c_double) => {
|
||||
parse_type!(noformat c_double);
|
||||
parse_type!(noformat c_double)
|
||||
};
|
||||
(c_float) => {
|
||||
parse_type!(noformat c_float);
|
||||
parse_type!(noformat c_float)
|
||||
};
|
||||
($type:ident) => {
|
||||
parse_type!($type, $type);
|
||||
parse_type!($type, $type)
|
||||
};
|
||||
($type:ident, $final:ty) => {{
|
||||
let n = if n.is_empty() {
|
||||
@@ -254,7 +264,7 @@ unsafe fn inner_scanf(
|
||||
$type::from_str_radix(&n, radix).map_err(|_| 0)?
|
||||
};
|
||||
if !ignore {
|
||||
*ap.arg::<*mut $final>() = n as $final;
|
||||
unsafe { *ap.next_arg::<*mut $final>() = n as $final };
|
||||
matched += 1;
|
||||
}
|
||||
}};
|
||||
@@ -266,10 +276,10 @@ unsafe fn inner_scanf(
|
||||
} else {
|
||||
parse_type!(c_float);
|
||||
}
|
||||
} else if c == b'p' {
|
||||
} else if c == 'p' {
|
||||
parse_type!(size_t, *mut c_void);
|
||||
} else {
|
||||
let unsigned = c == b'o' || c == b'u' || c == b'x' || c == b'X';
|
||||
let unsigned = c == 'o' || c == 'u' || c == 'x' || c == 'X';
|
||||
|
||||
match kind {
|
||||
IntKind::Byte => {
|
||||
@@ -325,99 +335,140 @@ unsafe fn inner_scanf(
|
||||
}
|
||||
}
|
||||
}
|
||||
b's' => {
|
||||
while (byte as char).is_whitespace() {
|
||||
if !read!() {
|
||||
return Ok(matched);
|
||||
}
|
||||
|
||||
's' => {
|
||||
macro_rules! parse_string_type {
|
||||
($type:ident) => {
|
||||
while character.is_whitespace() {
|
||||
if !read!() {
|
||||
return Ok(matched);
|
||||
}
|
||||
}
|
||||
|
||||
let mut ptr: Option<*mut $type> =
|
||||
if ignore { None } else { Some(ap.next_arg()) };
|
||||
|
||||
while width.map(|w| w > 0).unwrap_or(true) && !character.is_whitespace()
|
||||
{
|
||||
if let Some(ref mut ptr) = ptr {
|
||||
**ptr = character as $type;
|
||||
*ptr = ptr.offset(1);
|
||||
}
|
||||
width = width.map(|w| w - 1);
|
||||
if width.map(|w| w > 0).unwrap_or(true) && !read!() {
|
||||
eof = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we read the width, we end up in the last character of was
|
||||
// intended to be read, so we advance one
|
||||
if let Some(0) = width {
|
||||
read!();
|
||||
}
|
||||
|
||||
if let Some(ptr) = ptr {
|
||||
*ptr = 0;
|
||||
matched += 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let mut ptr: Option<*mut c_char> = if ignore { None } else { Some(ap.arg()) };
|
||||
|
||||
while width.map(|w| w > 0).unwrap_or(true) && !(byte as char).is_whitespace() {
|
||||
if let Some(ref mut ptr) = ptr {
|
||||
**ptr = byte as c_char;
|
||||
*ptr = ptr.offset(1);
|
||||
if c_kind == CharKind::Ascii {
|
||||
unsafe {
|
||||
parse_string_type!(c_char);
|
||||
}
|
||||
width = width.map(|w| w - 1);
|
||||
if width.map(|w| w > 0).unwrap_or(true) && !read!() {
|
||||
eof = true;
|
||||
break;
|
||||
} else {
|
||||
unsafe {
|
||||
parse_string_type!(wchar_t);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ptr) = ptr {
|
||||
*ptr = 0;
|
||||
matched += 1;
|
||||
r.commit();
|
||||
}
|
||||
}
|
||||
b'c' => {
|
||||
let ptr: Option<*mut c_char> = if ignore { None } else { Some(ap.arg()) };
|
||||
|
||||
for i in 0..width.unwrap_or(1) {
|
||||
if let Some(ptr) = ptr {
|
||||
*ptr.add(i) = byte as c_char;
|
||||
}
|
||||
width = width.map(|w| w - 1);
|
||||
if width.map(|w| w > 0).unwrap_or(true) && !read!() {
|
||||
eof = true;
|
||||
break;
|
||||
}
|
||||
'c' => {
|
||||
macro_rules! parse_char_type {
|
||||
($type:ident) => {
|
||||
let ptr: Option<*mut $type> = if ignore {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { ap.next_arg() })
|
||||
};
|
||||
|
||||
for i in 0..width.unwrap_or(1) {
|
||||
if let Some(ptr) = ptr {
|
||||
unsafe { *ptr.add(i) = character as $type };
|
||||
}
|
||||
width = width.map(|w| w - 1);
|
||||
if width.map(|w| w > 0).unwrap_or(true) && !read!() {
|
||||
eof = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ptr.is_some() {
|
||||
matched += 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if ptr.is_some() {
|
||||
matched += 1;
|
||||
r.commit();
|
||||
if c_kind == CharKind::Ascii {
|
||||
parse_char_type!(c_char);
|
||||
} else {
|
||||
parse_char_type!(wchar_t);
|
||||
}
|
||||
}
|
||||
b'[' => {
|
||||
c = next_byte(&mut format)?;
|
||||
|
||||
'[' => {
|
||||
c = next_char(&mut format)?;
|
||||
|
||||
let mut matches = Vec::new();
|
||||
let invert = if c == b'^' {
|
||||
c = next_byte(&mut format)?;
|
||||
let invert = if c == '^' {
|
||||
c = next_char(&mut format)?;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let mut prev;
|
||||
let mut prev: u32;
|
||||
loop {
|
||||
matches.push(c);
|
||||
prev = c;
|
||||
c = next_byte(&mut format)?;
|
||||
if c == b'-' {
|
||||
if prev == b']' {
|
||||
prev = c.into();
|
||||
c = next_char(&mut format)?;
|
||||
if c == '-' {
|
||||
if prev as u8 == b']' {
|
||||
continue;
|
||||
}
|
||||
c = next_byte(&mut format)?;
|
||||
if c == b']' {
|
||||
matches.push(b'-');
|
||||
c = next_char(&mut format)?;
|
||||
if c == ']' {
|
||||
matches.push('-');
|
||||
break;
|
||||
}
|
||||
prev += 1;
|
||||
while prev < c {
|
||||
matches.push(prev);
|
||||
while prev < c.into() {
|
||||
matches.push(char::try_from(prev).map_err(|_| -1)?);
|
||||
prev += 1;
|
||||
}
|
||||
} else if c == b']' {
|
||||
} else if c == ']' {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let mut ptr: Option<*mut c_char> = if ignore { None } else { Some(ap.arg()) };
|
||||
let mut ptr: Option<*mut c_char> = if ignore {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { ap.next_arg() })
|
||||
};
|
||||
|
||||
// While we haven't used up all the width, and it matches
|
||||
let mut data_stored = false;
|
||||
while width.map(|w| w > 0).unwrap_or(true) && !invert == matches.contains(&byte)
|
||||
while width.map(|w| w > 0).unwrap_or(true)
|
||||
&& invert != matches.contains(&character)
|
||||
{
|
||||
if let Some(ref mut ptr) = ptr {
|
||||
**ptr = byte as c_char;
|
||||
*ptr = ptr.offset(1);
|
||||
unsafe { **ptr = character as c_char };
|
||||
*ptr = unsafe { ptr.offset(1) };
|
||||
data_stored = true;
|
||||
}
|
||||
r.commit();
|
||||
// Decrease the width, and read a new character unless the width is 0
|
||||
width = width.map(|w| w - 1);
|
||||
if width.map(|w| w > 0).unwrap_or(true) && !read!() {
|
||||
@@ -429,13 +480,13 @@ unsafe fn inner_scanf(
|
||||
}
|
||||
|
||||
if data_stored {
|
||||
*ptr.unwrap() = 0;
|
||||
unsafe { *ptr.unwrap() = 0 };
|
||||
matched += 1;
|
||||
}
|
||||
}
|
||||
b'n' => {
|
||||
'n' => {
|
||||
if !ignore {
|
||||
*ap.arg::<*mut c_int>() = count as c_int;
|
||||
unsafe { *ap.next_arg::<*mut c_int>() = count as c_int };
|
||||
}
|
||||
}
|
||||
_ => return Err(-1),
|
||||
@@ -445,7 +496,7 @@ unsafe fn inner_scanf(
|
||||
return Ok(matched);
|
||||
}
|
||||
|
||||
if width != Some(0) && c != b'n' {
|
||||
if width != Some(0) && c != 'n' {
|
||||
// It didn't hit the width, so an extra character was read and matched.
|
||||
// But this character did not match so let's reuse it.
|
||||
skip_read = true;
|
||||
@@ -455,8 +506,8 @@ unsafe fn inner_scanf(
|
||||
Ok(matched)
|
||||
}
|
||||
|
||||
pub unsafe fn scanf(r: LookAheadReader, format: *const c_char, ap: va_list) -> c_int {
|
||||
match inner_scanf(r, format, ap) {
|
||||
pub unsafe fn scanf<T: Kind>(r: Reader<T>, format: Reader<T>, ap: va_list) -> c_int {
|
||||
match unsafe { inner_scanf(r, format, ap) } {
|
||||
Ok(n) => n,
|
||||
Err(n) => n,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user