diff --git a/src/header/stdio/lookaheadreader.rs b/src/header/stdio/lookaheadreader.rs deleted file mode 100644 index 3860f6f338..0000000000 --- a/src/header/stdio/lookaheadreader.rs +++ /dev/null @@ -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, 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, 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, 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())) - } -} diff --git a/src/header/stdio/mod.rs b/src/header/stdio/mod.rs index 087c989935..069d45562a 100644 --- a/src/header/stdio/mod.rs +++ b/src/header/stdio/mod.rs @@ -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 = f.into(); + unsafe { + let format = CStr::from_ptr(format); + scanf::scanf(reader, format.into(), ap) + } } /// See . @@ -1552,8 +1555,11 @@ pub unsafe extern "C" fn scanf(format: *const c_char, mut __valist: ...) -> c_in /// See . #[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::()).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 . @@ -1563,8 +1569,11 @@ pub unsafe extern "C" fn sscanf( format: *const c_char, mut __valist: ... ) -> c_int { - let reader = (s.cast::()).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() { diff --git a/src/header/stdio/reader.rs b/src/header/stdio/reader.rs new file mode 100644 index 0000000000..aaf6ea05e2 --- /dev/null +++ b/src/header/stdio/reader.rs @@ -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> for BufferReader<'a, Wide> { + fn from(buff: WStr<'a>) -> Self { + BufferReader { buf: buff } + } +} + +impl<'a> From> 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; + + fn next(&mut self) -> Option { + 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, +} + +impl<'a, T: Kind> FileReader<'a, T> { + // Gets the wchar at the current position + #[inline] + fn get_curret_char(&mut self) -> Result, 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::(), + 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 { + 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; + + fn next(&mut self) -> Option { + 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::, + } + } +} + +pub enum Reader<'a, T: Kind> { + FILE(FileReader<'a, T>, PhantomData), + BUFFER(BufferReader<'a, T>), +} + +impl<'a, T: Kind> Iterator for Reader<'a, T> { + type Item = Result; + + fn next(&mut self) -> Option { + 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::) + } +} + +impl<'a> From> for Reader<'a, Wide> { + fn from(buff: WStr<'a>) -> Self { + Self::BUFFER(buff.into()) + } +} + +impl<'a> From> for Reader<'a, Thin> { + fn from(buff: CStr<'a>) -> Self { + Self::BUFFER(buff.into()) + } +} diff --git a/src/header/stdio/scanf.rs b/src/header/stdio/scanf.rs index 18d79a0101..f7ff17f689 100644 --- a/src/header/stdio/scanf.rs +++ b/src/header/stdio/scanf.rs @@ -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 { - let c = unsafe { **string } as u8; - *string = unsafe { string.offset(1) }; - if c == 0 { Err(-1) } else { Ok(c) } +fn next_char(lar: &mut Peekable>) -> Result { + 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( + mut r: Reader, + format: Reader, mut ap: va_list, ) -> Result { 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(r: Reader, format: Reader, ap: va_list) -> c_int { match unsafe { inner_scanf(r, format, ap) } { Ok(n) => n, Err(n) => n, diff --git a/src/header/wchar/lookaheadreader.rs b/src/header/wchar/lookaheadreader.rs deleted file mode 100644 index 0c2490a5ba..0000000000 --- a/src/header/wchar/lookaheadreader.rs +++ /dev/null @@ -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, 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, 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::(), - 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, 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())) - } -} diff --git a/src/header/wchar/mod.rs b/src/header/wchar/mod.rs index 4a1db8ce88..b29e71a218 100644 --- a/src/header/wchar/mod.rs +++ b/src/header/wchar/mod.rs @@ -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 . #[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::()).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 . @@ -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 = f.into(); + + unsafe { + let format = WStr::from_ptr(format); + wscanf::scanf(reader, format.into(), __valist) + } } /// See . diff --git a/src/header/wchar/wscanf.rs b/src/header/wchar/wscanf.rs index 43b5c2aa85..e7797ed1b4 100644 --- a/src/header/wchar/wscanf.rs +++ b/src/header/wchar/wscanf.rs @@ -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 { - 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 { - 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::() { - 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::(r, format.into(), ap) } { Ok(n) => n, Err(n) => n, }