Merge branch 'lookaheadreader_in_format_iterator' into 'master'
Using Reader (formerly LookAheadReader) in scanf/wscanf format string See merge request redox-os/relibc!620
This commit is contained in:
@@ -1,99 +0,0 @@
|
||||
use super::{FILE, SEEK_SET, fseek_locked, ftell_locked};
|
||||
use crate::{io::Read, platform::types::off_t};
|
||||
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 as off_t, 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 as off_t, 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<'a> {
|
||||
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()))
|
||||
}
|
||||
}
|
||||
+19
-10
@@ -17,7 +17,7 @@ use core::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
c_str::CStr,
|
||||
c_str::{CStr, Thin},
|
||||
c_vec::CVec,
|
||||
error::{ResultExt, ResultExtPtrMut},
|
||||
fs::File,
|
||||
@@ -36,6 +36,7 @@ use crate::{
|
||||
types::{c_char, c_int, c_long, c_uint, c_ulonglong, c_void, off_t, size_t},
|
||||
},
|
||||
};
|
||||
use reader::Reader;
|
||||
|
||||
pub use self::constants::*;
|
||||
mod constants;
|
||||
@@ -48,10 +49,9 @@ mod getdelim;
|
||||
|
||||
mod ext;
|
||||
mod helpers;
|
||||
mod lookaheadreader;
|
||||
pub mod printf;
|
||||
mod scanf;
|
||||
use lookaheadreader::LookAheadReader;
|
||||
pub mod reader;
|
||||
pub mod scanf;
|
||||
static mut TMPNAM_BUF: [c_char; L_tmpnam as usize + 1] = [0; L_tmpnam as usize + 1];
|
||||
|
||||
enum Buffer<'a> {
|
||||
@@ -1523,8 +1523,11 @@ pub unsafe extern "C" fn vfscanf(file: *mut FILE, format: *const c_char, ap: va_
|
||||
}
|
||||
|
||||
let f: &mut FILE = &mut file;
|
||||
let reader: LookAheadReader = f.into();
|
||||
unsafe { scanf::scanf(reader, format, ap) }
|
||||
let reader: Reader<Thin> = f.into();
|
||||
unsafe {
|
||||
let format = CStr::from_ptr(format);
|
||||
scanf::scanf(reader, format.into(), ap)
|
||||
}
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fscanf.html>.
|
||||
@@ -1552,8 +1555,11 @@ pub unsafe extern "C" fn scanf(format: *const c_char, mut __valist: ...) -> c_in
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/vfscanf.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn vsscanf(s: *const c_char, format: *const c_char, ap: va_list) -> c_int {
|
||||
let reader = (s.cast::<u8>()).into();
|
||||
unsafe { scanf::scanf(reader, format, ap) }
|
||||
unsafe {
|
||||
let format = CStr::from_ptr(format);
|
||||
let s = CStr::from_ptr(s);
|
||||
scanf::scanf(s.into(), format.into(), ap)
|
||||
}
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fscanf.html>.
|
||||
@@ -1563,8 +1569,11 @@ pub unsafe extern "C" fn sscanf(
|
||||
format: *const c_char,
|
||||
mut __valist: ...
|
||||
) -> c_int {
|
||||
let reader = (s.cast::<u8>()).into();
|
||||
unsafe { scanf::scanf(reader, format, __valist.as_va_list()) }
|
||||
unsafe {
|
||||
let format = CStr::from_ptr(format);
|
||||
let s = CStr::from_ptr(s);
|
||||
scanf::scanf(s.into(), format.into(), __valist.as_va_list())
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn flush_io_streams() {
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
+177
-144
@@ -1,50 +1,56 @@
|
||||
use super::lookaheadreader::LookAheadReader;
|
||||
use crate::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,
|
||||
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 = unsafe { **string } as u8;
|
||||
*string = unsafe { string.offset(1) };
|
||||
if c == 0 { Err(-1) } else { Ok(c) }
|
||||
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('\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),
|
||||
}
|
||||
}};
|
||||
}
|
||||
@@ -70,38 +76,37 @@ unsafe fn inner_scanf(
|
||||
}
|
||||
}
|
||||
|
||||
while unsafe { *format } != 0 {
|
||||
let mut c = unsafe { 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 = unsafe { next_byte(&mut format) }?;
|
||||
c = next_char(&mut format)?;
|
||||
|
||||
let mut ignore = false;
|
||||
if c == b'*' {
|
||||
if c == '*' {
|
||||
ignore = true;
|
||||
c = unsafe { next_byte(&mut format) }?;
|
||||
c = next_char(&mut format)?;
|
||||
}
|
||||
|
||||
let mut width = String::new();
|
||||
while c.is_ascii_digit() {
|
||||
width.push(c as char);
|
||||
c = unsafe { next_byte(&mut format) }?;
|
||||
width.push(c);
|
||||
c = next_char(&mut format)?;
|
||||
}
|
||||
let mut width = if width.is_empty() {
|
||||
None
|
||||
@@ -117,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 = unsafe { 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,
|
||||
};
|
||||
|
||||
@@ -183,16 +195,16 @@ unsafe fn inner_scanf(
|
||||
let mut dot = false;
|
||||
|
||||
while width.map(|w| w > 0).unwrap_or(true)
|
||||
&& ((b'0'..=b'7').contains(&byte)
|
||||
|| (radix >= 10 && (b'8'..=b'9').contains(&byte))
|
||||
|| (float && !dot && byte == b'.')
|
||||
&& (('0'..='7').contains(&character)
|
||||
|| (radix >= 10 && ('8'..='9').contains(&character))
|
||||
|| (float && !dot && character == '.')
|
||||
|| (radix == 16
|
||||
&& ((b'a'..=b'f').contains(&byte)
|
||||
|| (b'A'..=b'F').contains(&byte))))
|
||||
&& (('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 {
|
||||
@@ -200,11 +212,10 @@ unsafe fn inner_scanf(
|
||||
}
|
||||
width = width.map(|w| w - 1);
|
||||
if !read!() {
|
||||
// 0 by itself, will be handled below
|
||||
break;
|
||||
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,12 +225,11 @@ 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!() {
|
||||
break;
|
||||
@@ -254,7 +264,7 @@ unsafe fn inner_scanf(
|
||||
$type::from_str_radix(&n, radix).map_err(|_| 0)?
|
||||
};
|
||||
if !ignore {
|
||||
unsafe { *ap.arg::<*mut $final>() = n as $final};
|
||||
unsafe { *ap.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,91 +335,114 @@ 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.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 let Some(ptr) = ptr {
|
||||
*ptr = 0;
|
||||
matched += 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let mut ptr: Option<*mut c_char> = if ignore {
|
||||
None
|
||||
if c_kind == CharKind::Ascii {
|
||||
unsafe {
|
||||
parse_string_type!(c_char);
|
||||
}
|
||||
} else {
|
||||
Some(unsafe { ap.arg() })
|
||||
};
|
||||
|
||||
while width.map(|w| w > 0).unwrap_or(true) && !(byte as char).is_whitespace() {
|
||||
if let Some(ref mut ptr) = ptr {
|
||||
unsafe { **ptr = byte as c_char };
|
||||
*ptr = unsafe { ptr.offset(1) };
|
||||
unsafe {
|
||||
parse_string_type!(wchar_t);
|
||||
}
|
||||
width = width.map(|w| w - 1);
|
||||
if width.map(|w| w > 0).unwrap_or(true) && !read!() {
|
||||
eof = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ptr) = ptr {
|
||||
unsafe { *ptr = 0 };
|
||||
matched += 1;
|
||||
r.commit();
|
||||
}
|
||||
}
|
||||
b'c' => {
|
||||
let ptr: Option<*mut c_char> = if ignore {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { ap.arg() })
|
||||
};
|
||||
|
||||
for i in 0..width.unwrap_or(1) {
|
||||
if let Some(ptr) = ptr {
|
||||
unsafe { *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.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 = unsafe { next_byte(&mut format) }?;
|
||||
|
||||
'[' => {
|
||||
c = next_char(&mut format)?;
|
||||
|
||||
let mut matches = Vec::new();
|
||||
let invert = if c == b'^' {
|
||||
c = unsafe { 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 = unsafe { 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 = unsafe { 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;
|
||||
}
|
||||
}
|
||||
@@ -422,14 +455,14 @@ unsafe fn inner_scanf(
|
||||
|
||||
// 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 {
|
||||
unsafe { **ptr = byte as c_char };
|
||||
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!() {
|
||||
@@ -445,7 +478,7 @@ unsafe fn inner_scanf(
|
||||
matched += 1;
|
||||
}
|
||||
}
|
||||
b'n' => {
|
||||
'n' => {
|
||||
if !ignore {
|
||||
unsafe { *ap.arg::<*mut c_int>() = count as c_int };
|
||||
}
|
||||
@@ -457,7 +490,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;
|
||||
@@ -467,7 +500,7 @@ unsafe fn inner_scanf(
|
||||
Ok(matched)
|
||||
}
|
||||
|
||||
pub unsafe fn scanf(r: LookAheadReader, format: *const c_char, ap: va_list) -> c_int {
|
||||
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,
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
use super::{FILE, SEEK_SET, fseek_locked, ftell_locked};
|
||||
use crate::{
|
||||
header::{
|
||||
errno::EILSEQ,
|
||||
wchar::{MB_CUR_MAX, get_char_encoded_length, mbrtowc},
|
||||
wctype::WEOF,
|
||||
},
|
||||
io::Read,
|
||||
platform::{
|
||||
ERRNO,
|
||||
types::{c_char, off_t, wchar_t, wint_t},
|
||||
},
|
||||
};
|
||||
use core::ptr;
|
||||
|
||||
struct LookAheadBuffer {
|
||||
buf: *const wint_t,
|
||||
pos: isize,
|
||||
look_ahead: isize,
|
||||
}
|
||||
|
||||
impl LookAheadBuffer {
|
||||
fn look_ahead(&mut self) -> Result<Option<wint_t>, i32> {
|
||||
let wchar = unsafe { *self.buf.offset(self.look_ahead) };
|
||||
if wchar == 0 {
|
||||
Ok(None)
|
||||
} else {
|
||||
self.look_ahead += 1;
|
||||
Ok(Some(wchar))
|
||||
}
|
||||
}
|
||||
|
||||
fn commit(&mut self) {
|
||||
self.pos = self.look_ahead;
|
||||
}
|
||||
}
|
||||
|
||||
impl From<*const wint_t> for LookAheadBuffer {
|
||||
fn from(buff: *const wint_t) -> 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<wint_t>, i32> {
|
||||
let buf = &mut [0; MB_CUR_MAX as usize];
|
||||
let seek = unsafe { ftell_locked(self.f) };
|
||||
unsafe { fseek_locked(self.f, self.look_ahead as off_t, SEEK_SET) };
|
||||
|
||||
let mut encoded_length = 0;
|
||||
let mut bytes_read = 0;
|
||||
loop {
|
||||
match self.f.read(&mut buf[bytes_read..bytes_read + 1]) {
|
||||
Ok(0) => {
|
||||
ERRNO.set(EILSEQ);
|
||||
return Ok(Some(WEOF));
|
||||
}
|
||||
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 Ok(Some(WEOF));
|
||||
};
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
|
||||
fseek_locked(self.f, seek, SEEK_SET);
|
||||
}
|
||||
|
||||
self.look_ahead += encoded_length as i64;
|
||||
|
||||
Ok(Some(wc as wint_t))
|
||||
}
|
||||
|
||||
fn commit(&mut self) {
|
||||
unsafe { fseek_locked(self.f, self.look_ahead as off_t, 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(LookAheadBuffer),
|
||||
}
|
||||
|
||||
pub struct LookAheadReader<'a>(LookAheadReaderEnum<'a>);
|
||||
|
||||
impl LookAheadReader<'_> {
|
||||
pub fn lookahead1(&mut self) -> Result<Option<wint_t>, 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<'a> {
|
||||
LookAheadReader(LookAheadReaderEnum::FILE(f.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<*const wint_t> for LookAheadReader<'a> {
|
||||
fn from(buff: *const wint_t) -> LookAheadReader<'a> {
|
||||
LookAheadReader(LookAheadReaderEnum::BUFFER(buff.into()))
|
||||
}
|
||||
}
|
||||
+15
-7
@@ -5,7 +5,7 @@
|
||||
use core::{char, ffi::VaList as va_list, mem, ptr, slice};
|
||||
|
||||
use crate::{
|
||||
c_str::WStr,
|
||||
c_str::{WStr, Wide},
|
||||
header::{
|
||||
ctype::isspace,
|
||||
errno::{EILSEQ, ENOMEM, ERANGE},
|
||||
@@ -13,7 +13,7 @@ use crate::{
|
||||
stdlib::{MB_CUR_MAX, MB_LEN_MAX, malloc},
|
||||
string,
|
||||
time::*,
|
||||
wchar::{lookaheadreader::LookAheadReader, utf8::get_char_encoded_length},
|
||||
wchar::reader::Reader,
|
||||
wctype::*,
|
||||
},
|
||||
iter::{NulTerminated, NulTerminatedInclusive},
|
||||
@@ -26,11 +26,12 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
mod lookaheadreader;
|
||||
mod utf8;
|
||||
mod wprintf;
|
||||
mod wscanf;
|
||||
|
||||
pub use utf8::get_char_encoded_length;
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/wchar.h.html>.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -327,8 +328,11 @@ pub unsafe extern "C" fn vswscanf(
|
||||
format: *const wchar_t,
|
||||
__valist: va_list,
|
||||
) -> c_int {
|
||||
let reader = (s.cast::<wint_t>()).into();
|
||||
unsafe { wscanf::scanf(reader, format, __valist) }
|
||||
unsafe {
|
||||
let format = WStr::from_ptr(format);
|
||||
let s = WStr::from_ptr(s);
|
||||
wscanf::scanf(s.into(), format.into(), __valist)
|
||||
}
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fwscanf.html>.
|
||||
@@ -1031,8 +1035,12 @@ pub unsafe extern "C" fn vfwscanf(
|
||||
}
|
||||
|
||||
let f: &mut FILE = &mut file;
|
||||
let reader: LookAheadReader = f.into();
|
||||
unsafe { wscanf::scanf(reader, format, __valist) }
|
||||
let reader: Reader<Wide> = f.into();
|
||||
|
||||
unsafe {
|
||||
let format = WStr::from_ptr(format);
|
||||
wscanf::scanf(reader, format.into(), __valist)
|
||||
}
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/vwscanf.html>.
|
||||
|
||||
+6
-521
@@ -1,527 +1,12 @@
|
||||
use super::lookaheadreader::LookAheadReader;
|
||||
use crate::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,
|
||||
wint_t,
|
||||
use crate::{
|
||||
c_str::{self, WStr},
|
||||
header::stdio::{reader::Reader, scanf::inner_scanf},
|
||||
platform::types::c_int,
|
||||
};
|
||||
use alloc::{string::String, vec::Vec};
|
||||
use core::ffi::VaList as va_list;
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
enum IntKind {
|
||||
Byte,
|
||||
Short,
|
||||
Int,
|
||||
Long,
|
||||
LongLong,
|
||||
IntMax,
|
||||
PtrDiff,
|
||||
Size,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
enum CharKind {
|
||||
Ascii,
|
||||
Wide,
|
||||
}
|
||||
|
||||
/// Helper function for progressing a C string
|
||||
unsafe fn next_char(string: &mut *const wchar_t) -> Result<wint_t, c_int> {
|
||||
let c = unsafe { **string } as wint_t;
|
||||
*string = unsafe { string.offset(1) };
|
||||
if c == 0 { Err(-1) } else { Ok(c) }
|
||||
}
|
||||
|
||||
macro_rules! wc_as_char {
|
||||
($c:ident) => {
|
||||
char::try_from($c).map_err(|_| -1)?
|
||||
};
|
||||
}
|
||||
|
||||
unsafe fn inner_scanf(
|
||||
mut r: LookAheadReader,
|
||||
mut format: *const wchar_t,
|
||||
mut ap: va_list,
|
||||
) -> Result<c_int, c_int> {
|
||||
let mut matched = 0;
|
||||
let mut wchar = 0;
|
||||
let mut skip_read = false;
|
||||
let mut count = 0;
|
||||
|
||||
macro_rules! read {
|
||||
() => {{
|
||||
match r.lookahead1() {
|
||||
Ok(None) => false,
|
||||
Ok(Some(b)) => {
|
||||
wchar = b;
|
||||
count += 1;
|
||||
true
|
||||
}
|
||||
Err(x) => return Err(x),
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! maybe_read {
|
||||
() => {
|
||||
maybe_read!(inner false);
|
||||
};
|
||||
(noreset) => {
|
||||
maybe_read!(inner);
|
||||
};
|
||||
(inner $($placeholder:expr)*) => {
|
||||
if !skip_read && !read!() {
|
||||
match matched {
|
||||
0 => return Ok(-1),
|
||||
a => return Ok(a),
|
||||
}
|
||||
}
|
||||
$(else {
|
||||
// Hacky way of having this optional
|
||||
skip_read = $placeholder;
|
||||
})*
|
||||
}
|
||||
}
|
||||
|
||||
while unsafe { *format } != 0 {
|
||||
let mut c = unsafe { next_char(&mut format) }?;
|
||||
|
||||
if c as u8 == b' ' {
|
||||
maybe_read!(noreset);
|
||||
|
||||
while (wc_as_char!(wchar)).is_whitespace() {
|
||||
if !read!() {
|
||||
return Ok(matched);
|
||||
}
|
||||
}
|
||||
|
||||
skip_read = true;
|
||||
} else if c as u8 != b'%' {
|
||||
maybe_read!();
|
||||
if c != wchar {
|
||||
return Ok(matched);
|
||||
}
|
||||
r.commit();
|
||||
} else {
|
||||
c = unsafe { next_char(&mut format) }?;
|
||||
|
||||
let mut ignore = false;
|
||||
if c as u8 == b'*' {
|
||||
ignore = true;
|
||||
c = unsafe { next_char(&mut format) }?;
|
||||
}
|
||||
|
||||
let mut width = String::new();
|
||||
while c as u8 >= b'0' && c as u8 <= b'9' {
|
||||
width.push(wc_as_char!(c));
|
||||
c = unsafe { next_char(&mut format) }?;
|
||||
}
|
||||
let mut width = if width.is_empty() {
|
||||
None
|
||||
} else {
|
||||
match width.parse::<usize>() {
|
||||
Ok(n) => Some(n),
|
||||
Err(_) => return Err(-1),
|
||||
}
|
||||
};
|
||||
|
||||
// When an EOF occurs, eof is set, stuff is marked matched
|
||||
// as usual, and finally it is returned
|
||||
let mut eof = false;
|
||||
|
||||
let mut kind = IntKind::Int;
|
||||
let mut c_kind = CharKind::Ascii;
|
||||
loop {
|
||||
match c as u8 {
|
||||
b'h' => {
|
||||
if kind == IntKind::Short || kind == IntKind::Byte {
|
||||
kind = IntKind::Byte;
|
||||
} else {
|
||||
kind = IntKind::Short;
|
||||
}
|
||||
}
|
||||
b'j' => kind = IntKind::IntMax,
|
||||
b'l' => {
|
||||
if kind == IntKind::Long || kind == IntKind::LongLong {
|
||||
kind = IntKind::LongLong;
|
||||
} else {
|
||||
kind = IntKind::Long;
|
||||
}
|
||||
}
|
||||
b'q' | b'L' => kind = IntKind::LongLong,
|
||||
b't' => kind = IntKind::PtrDiff,
|
||||
b'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
|
||||
b'c' | b's' if kind == IntKind::Long => {
|
||||
c_kind = CharKind::Wide;
|
||||
break;
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
|
||||
c = unsafe { next_char(&mut format) }?;
|
||||
}
|
||||
|
||||
if c as u8 != b'n' {
|
||||
maybe_read!(noreset);
|
||||
}
|
||||
match c as u8 {
|
||||
b'%' => {
|
||||
while (wc_as_char!(wchar)).is_whitespace() {
|
||||
if !read!() {
|
||||
return Ok(matched);
|
||||
}
|
||||
}
|
||||
|
||||
if wchar as u8 != b'%' {
|
||||
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 (wc_as_char!(wchar)).is_whitespace() {
|
||||
if !read!() {
|
||||
return Ok(matched);
|
||||
}
|
||||
}
|
||||
|
||||
let pointer = c as u8 == b'p';
|
||||
// Pointers aren't automatic, but we do want to parse "0x"
|
||||
let auto = c as u8 == b'i' || pointer;
|
||||
let float = c as u8 == b'f'
|
||||
|| c as u8 == b'e'
|
||||
|| c as u8 == b'g'
|
||||
|| c as u8 == b'E'
|
||||
|| c as u8 == b'a';
|
||||
|
||||
let mut radix = match c as u8 {
|
||||
b'o' => 8,
|
||||
b'x' | b'X' | b'p' => 16,
|
||||
_ => 10,
|
||||
};
|
||||
|
||||
let mut n = String::new();
|
||||
let mut dot = false;
|
||||
|
||||
while width.map(|w| w > 0).unwrap_or(true)
|
||||
&& ((wchar as u8 >= b'0' && wchar as u8 <= b'7')
|
||||
|| (radix >= 10 && (wchar as u8 >= b'8' && wchar as u8 <= b'9'))
|
||||
|| (float && !dot && wchar as u8 == b'.')
|
||||
|| (radix == 16
|
||||
&& ((wchar as u8 >= b'a' && wchar as u8 <= b'f')
|
||||
|| (wchar as u8 >= b'A' && wchar as u8 <= b'F'))))
|
||||
{
|
||||
if auto
|
||||
&& n.is_empty()
|
||||
&& wchar as u8 == b'0'
|
||||
&& width.map(|w| w > 0).unwrap_or(true)
|
||||
{
|
||||
if !pointer {
|
||||
radix = 8;
|
||||
}
|
||||
width = width.map(|w| w - 1);
|
||||
if !read!() {
|
||||
return Ok(matched);
|
||||
}
|
||||
if width.map(|w| w > 0).unwrap_or(true)
|
||||
&& (wchar as u8 == b'x' || wchar as u8 == b'X')
|
||||
{
|
||||
radix = 16;
|
||||
width = width.map(|w| w - 1);
|
||||
if width.map(|w| w > 0).unwrap_or(true) && !read!() {
|
||||
return Ok(matched);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if wchar as u8 == b'.' {
|
||||
// Don't allow another dot
|
||||
dot = true;
|
||||
}
|
||||
n.push(wc_as_char!(wchar));
|
||||
r.commit();
|
||||
width = width.map(|w| w - 1);
|
||||
if width.map(|w| w > 0).unwrap_or(true) && !read!() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! parse_type {
|
||||
(noformat $type:ident) => {{
|
||||
let n = if n.is_empty() {
|
||||
0 as $type
|
||||
} else {
|
||||
n.parse::<$type>().map_err(|_| 0)?
|
||||
};
|
||||
if !ignore {
|
||||
unsafe { *ap.arg::<*mut $type>() = n };
|
||||
matched += 1;
|
||||
}
|
||||
}};
|
||||
(c_double) => {
|
||||
parse_type!(noformat c_double)
|
||||
};
|
||||
(c_float) => {
|
||||
parse_type!(noformat c_float)
|
||||
};
|
||||
($type:ident) => {
|
||||
parse_type!($type, $type)
|
||||
};
|
||||
($type:ident, $final:ty) => {{
|
||||
let n = if n.is_empty() {
|
||||
0 as $type
|
||||
} else {
|
||||
$type::from_str_radix(&n, radix).map_err(|_| 0)?
|
||||
};
|
||||
if !ignore {
|
||||
unsafe { *ap.arg::<*mut $final>() = n as $final };
|
||||
matched += 1;
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
if float {
|
||||
if kind == IntKind::Long || kind == IntKind::LongLong {
|
||||
parse_type!(c_double);
|
||||
} else {
|
||||
parse_type!(c_float);
|
||||
}
|
||||
} else if c as u8 == b'p' {
|
||||
parse_type!(size_t, *mut c_void);
|
||||
} else {
|
||||
let unsigned = c as u8 == b'o'
|
||||
|| c as u8 == b'u'
|
||||
|| c as u8 == b'x'
|
||||
|| c as u8 == b'X';
|
||||
|
||||
match kind {
|
||||
IntKind::Byte => {
|
||||
if unsigned {
|
||||
parse_type!(c_uchar);
|
||||
} else {
|
||||
parse_type!(c_char);
|
||||
}
|
||||
}
|
||||
IntKind::Short => {
|
||||
if unsigned {
|
||||
parse_type!(c_ushort)
|
||||
} else {
|
||||
parse_type!(c_short)
|
||||
}
|
||||
}
|
||||
IntKind::Int => {
|
||||
if unsigned {
|
||||
parse_type!(c_uint)
|
||||
} else {
|
||||
parse_type!(c_int)
|
||||
}
|
||||
}
|
||||
IntKind::Long => {
|
||||
if unsigned {
|
||||
parse_type!(c_ulong)
|
||||
} else {
|
||||
parse_type!(c_long)
|
||||
}
|
||||
}
|
||||
IntKind::LongLong => {
|
||||
if unsigned {
|
||||
parse_type!(c_ulonglong)
|
||||
} else {
|
||||
parse_type!(c_longlong)
|
||||
}
|
||||
}
|
||||
IntKind::IntMax => {
|
||||
if unsigned {
|
||||
parse_type!(uintmax_t)
|
||||
} else {
|
||||
parse_type!(intmax_t)
|
||||
}
|
||||
}
|
||||
IntKind::PtrDiff => parse_type!(ptrdiff_t),
|
||||
IntKind::Size => {
|
||||
if unsigned {
|
||||
parse_type!(size_t)
|
||||
} else {
|
||||
parse_type!(ssize_t)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b's' => {
|
||||
macro_rules! parse_string_type {
|
||||
($type:ident) => {
|
||||
while (wc_as_char!(wchar)).is_whitespace() {
|
||||
if !read!() {
|
||||
return Ok(matched);
|
||||
}
|
||||
}
|
||||
|
||||
let mut ptr: Option<*mut $type> =
|
||||
if ignore { None } else { Some(ap.arg()) };
|
||||
|
||||
while width.map(|w| w > 0).unwrap_or(true)
|
||||
&& !(wc_as_char!(wchar)).is_whitespace()
|
||||
{
|
||||
if let Some(ref mut ptr) = ptr {
|
||||
**ptr = wchar 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 let Some(ptr) = ptr {
|
||||
*ptr = 0;
|
||||
matched += 1;
|
||||
r.commit();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if c_kind == CharKind::Ascii {
|
||||
unsafe {
|
||||
parse_string_type!(c_char);
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
parse_string_type!(wchar_t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b'c' => {
|
||||
macro_rules! parse_char_type {
|
||||
($type:ident) => {
|
||||
let ptr: Option<*mut $type> = if ignore {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { ap.arg() })
|
||||
};
|
||||
|
||||
for i in 0..width.unwrap_or(1) {
|
||||
if let Some(ptr) = ptr {
|
||||
unsafe { *ptr.add(i) = wchar 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;
|
||||
r.commit();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if c_kind == CharKind::Ascii {
|
||||
parse_char_type!(c_char);
|
||||
} else {
|
||||
parse_char_type!(wchar_t);
|
||||
}
|
||||
}
|
||||
|
||||
b'[' => {
|
||||
c = unsafe { next_char(&mut format) }?;
|
||||
|
||||
let mut matches = Vec::new();
|
||||
let invert = if c as u8 == b'^' {
|
||||
c = unsafe { next_char(&mut format) }?;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let mut prev;
|
||||
loop {
|
||||
matches.push(c);
|
||||
prev = c;
|
||||
c = unsafe { next_char(&mut format) }?;
|
||||
if c as u8 == b'-' {
|
||||
if prev as u8 == b']' {
|
||||
continue;
|
||||
}
|
||||
c = unsafe { next_char(&mut format) }?;
|
||||
if c as u8 == b']' {
|
||||
matches.push('-' as wint_t);
|
||||
break;
|
||||
}
|
||||
prev += 1;
|
||||
while prev < c {
|
||||
matches.push(prev);
|
||||
prev += 1;
|
||||
}
|
||||
} else if c as u8 == b']' {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let mut ptr: Option<*mut c_char> = if ignore {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { ap.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(&wchar)
|
||||
{
|
||||
if let Some(ref mut ptr) = ptr {
|
||||
unsafe { **ptr = wchar 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!() {
|
||||
// Reading a new character has failed, return after
|
||||
// actually marking this as matched
|
||||
eof = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if data_stored {
|
||||
unsafe { *ptr.unwrap() = 0 };
|
||||
matched += 1;
|
||||
}
|
||||
}
|
||||
b'n' => {
|
||||
if !ignore {
|
||||
unsafe { *ap.arg::<*mut c_int>() = count as c_int };
|
||||
}
|
||||
}
|
||||
_ => return Err(-1),
|
||||
}
|
||||
|
||||
if eof {
|
||||
return Ok(matched);
|
||||
}
|
||||
|
||||
if width != Some(0) && c as u8 != b'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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(matched)
|
||||
}
|
||||
|
||||
pub unsafe fn scanf(r: LookAheadReader, format: *const wchar_t, ap: va_list) -> c_int {
|
||||
match unsafe { inner_scanf(r, format, ap) } {
|
||||
pub unsafe fn scanf(r: Reader<'_, c_str::Wide>, format: WStr, ap: va_list) -> c_int {
|
||||
match unsafe { inner_scanf::<c_str::Wide>(r, format.into(), ap) } {
|
||||
Ok(n) => n,
|
||||
Err(n) => n,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user