start strtok

This commit is contained in:
nora 2023-10-08 20:47:23 +02:00
parent 4d4af78afe
commit dfdf45b5c7
17 changed files with 158 additions and 76 deletions

View file

@ -1,17 +1,17 @@
use core::{ffi::CStr, ptr::NonNull};
use crate::{println, utils::SharedThinCstr};
use crate::{println, utils::CStrRef};
mod global {
use core::{cell::UnsafeCell, ptr::NonNull};
use crate::utils::{SharedThinCstr, SyncUnsafeCell};
use crate::utils::{CStrRef, SyncUnsafeCell};
use super::EnvP;
static ENVP: SyncUnsafeCell<EnvP> = SyncUnsafeCell(UnsafeCell::new(EnvP(None)));
pub(super) unsafe fn init(envp: *mut Option<SharedThinCstr<'static>>) {
pub(super) unsafe fn init(envp: *mut Option<CStrRef<'static>>) {
assert!((*ENVP.0.get()).0.is_none());
*ENVP.0.get() = EnvP(Some(NonNull::new(envp).unwrap()));
}
@ -23,21 +23,21 @@ mod global {
}
}
pub(crate) unsafe fn init(envp: *mut Option<SharedThinCstr<'static>>) {
pub(crate) unsafe fn init(envp: *mut Option<CStrRef<'static>>) {
global::init(envp);
}
#[derive(Clone, Copy)]
struct EnvP(Option<NonNull<Option<SharedThinCstr<'static>>>>);
struct EnvP(Option<NonNull<Option<CStrRef<'static>>>>);
unsafe impl Sync for EnvP {}
impl Iterator for EnvP {
type Item = SharedThinCstr<'static>;
type Item = CStrRef<'static>;
fn next(&mut self) -> Option<Self::Item> {
unsafe {
let value: Option<SharedThinCstr<'static>> = self.0.unwrap().as_ptr().read();
let value: Option<CStrRef<'static>> = self.0.unwrap().as_ptr().read();
value.map(|value| {
self.0 = Some(NonNull::new_unchecked(self.0.unwrap().as_ptr().add(1)));
@ -57,11 +57,11 @@ pub(crate) fn debug_env() {
println!("end vars");
}
pub fn getenv(name: SharedThinCstr<'_>) -> Option<SharedThinCstr<'static>> {
pub fn getenv(name: CStrRef<'_>) -> Option<CStrRef<'static>> {
getenv_inner(global::get(), name)
}
fn getenv_inner(mut envp: EnvP, name: SharedThinCstr<'_>) -> Option<SharedThinCstr<'static>> {
fn getenv_inner(mut envp: EnvP, name: CStrRef<'_>) -> Option<CStrRef<'static>> {
let mut eq_idx = 0;
envp.find(|env| {
// Find ENV
@ -99,7 +99,7 @@ mod tests {
use std::string::ToString;
use std::{ffi::CString, vec::Vec};
use crate::utils::{cstr, SharedThinCstr};
use crate::utils::{cstr, CStrRef};
use super::EnvP;
@ -109,9 +109,9 @@ mod tests {
.map(|s| CString::new(s.to_string()).unwrap())
.collect::<Vec<_>>();
let mut envs: Vec<Option<SharedThinCstr<'static>>> = cstrs
let mut envs: Vec<Option<CStrRef<'static>>> = cstrs
.iter()
.map(|cstr| unsafe { Some(SharedThinCstr::from_raw(cstr.as_ptr() as _)) })
.map(|cstr| unsafe { Some(CStrRef::from_raw(cstr.as_ptr() as _)) })
.collect();
envs.push(None);

View file

@ -1,10 +1,10 @@
use core::ffi::{c_int, c_long};
use crate::{error::Error, utils::SharedThinCstr};
use crate::{error::Error, utils::CStrRef};
pub fn parse_long<'a>(
str: SharedThinCstr<'a>,
endptr: Option<&mut Option<SharedThinCstr<'a>>>,
str: CStrRef<'a>,
endptr: Option<&mut Option<CStrRef<'a>>>,
base: c_int,
) -> Result<c_long, Error> {
if base != 10 {
@ -83,9 +83,9 @@ pub fn parse_long<'a>(
#[cfg(test)]
mod tests {
use crate::{cstr, utils::SharedThinCstr};
use crate::{cstr, utils::CStrRef};
fn test_strtol(str: SharedThinCstr<'_>, expected: i64, base: i32, parsed_len: usize) {
fn test_strtol(str: CStrRef<'_>, expected: i64, base: i32, parsed_len: usize) {
let mut end = None;
let result = super::parse_long(str, Some(&mut end), base).unwrap();
assert_eq!(result, expected);

View file

@ -1,10 +1,10 @@
use core::ffi::VaList;
use crate::{io::IoWrite, utils::SharedThinCstr, error::Error};
use crate::{io::IoWrite, utils::CStrRef, error::Error};
pub unsafe fn printf_generic(
mut sink: impl IoWrite,
format: SharedThinCstr<'_>,
format: CStrRef<'_>,
mut args: VaList<'_, '_>,
) -> Result<(), Error> {
let mut chars = format.into_iter();
@ -47,11 +47,11 @@ mod tests {
use std::string::String;
use std::vec::Vec;
use crate::utils::{cstr, SharedThinCstr};
use crate::utils::{cstr, CStrRef};
use super::printf_generic;
unsafe extern "C" fn test_printf(expected: &str, fmt: SharedThinCstr<'_>, mut args: ...) {
unsafe extern "C" fn test_printf(expected: &str, fmt: CStrRef<'_>, mut args: ...) {
let mut sink = Vec::new();
printf_generic(&mut sink, fmt, args.as_va_list()).unwrap();

View file

@ -1,17 +1,17 @@
use crate::{
error::{Error, SyscallResultExt},
utils::SharedThinCstr,
utils::CStrRef,
};
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Fd(pub i32);
pub fn open(arg: SharedThinCstr<'_>, flags: i32) -> Result<Fd, Error> {
pub fn open(arg: CStrRef<'_>, flags: i32) -> Result<Fd, Error> {
sys_open(arg, flags)
}
pub fn sys_open(arg: SharedThinCstr<'_>, flags: i32) -> Result<Fd, Error> {
pub fn sys_open(arg: CStrRef<'_>, flags: i32) -> Result<Fd, Error> {
unsafe {
crate::syscall::syscall!(crate::syscall::SYS_OPEN, arg.as_raw(), flags).syscall_resultify()
}

View file

@ -1,4 +1,4 @@
use crate::{io::fd, utils::SharedThinCstr};
use crate::{io::fd, utils::CStrRef};
#[derive(Debug, PartialEq)]
pub enum OpenMode {
@ -22,7 +22,7 @@ impl OpenMode {
}
}
pub fn parse(str: SharedThinCstr<'_>) -> Result<OpenMode, &'static str> {
pub fn parse(str: CStrRef<'_>) -> Result<OpenMode, &'static str> {
let mut buf = [0; 2];
let mut i = 0;

View file

@ -4,7 +4,7 @@ use crate::{
alloc,
error::Error,
io::{fd, stream::file::OpenMode},
utils::SharedThinCstr,
utils::CStrRef,
};
use super::{fd::Fd, IoWrite, EOF};
@ -32,8 +32,8 @@ impl IoWrite for &FileStream {
}
pub unsafe fn fopen<'a>(
pathname: SharedThinCstr<'_>,
mode: SharedThinCstr<'_>,
pathname: CStrRef<'_>,
mode: CStrRef<'_>,
) -> Result<&'a FileStream, Error> {
let Ok(mode) = OpenMode::parse(mode) else {
return Err(Error::INVAL);

View file

@ -16,6 +16,7 @@ pub mod io;
pub mod mem;
pub mod misc;
pub mod start;
pub mod string;
mod stubs;
mod sys;
pub mod utils;

View file

@ -1,4 +1,4 @@
use crate::utils::SharedThinCstr;
use crate::utils::CStrRef;
#[inline]
pub unsafe fn memset(ptr: *mut u8, constant: u8, len: usize) {
@ -59,13 +59,13 @@ pub unsafe fn memcmp(s1: *const u8, s2: *const u8, size: usize) -> i32 {
}
#[inline]
pub unsafe fn strcmp(s1: SharedThinCstr<'_>, s2: SharedThinCstr<'_>) -> i32 {
pub unsafe fn strcmp(s1: CStrRef<'_>, s2: CStrRef<'_>) -> i32 {
s1.into_iter().cmp(s2) as i8 as i32
}
// This technically violates the safety precondition of SharedThinCstr but that's fine, we're careful.
#[inline]
pub unsafe fn strncmp(s1: SharedThinCstr<'_>, s2: SharedThinCstr<'_>, size: usize) -> i32 {
pub unsafe fn strncmp(s1: CStrRef<'_>, s2: CStrRef<'_>, size: usize) -> i32 {
s1.into_iter().take(size).cmp(s2.into_iter().take(size)) as i8 as i32
}
@ -81,7 +81,7 @@ pub unsafe fn strlen(mut s: *const u8) -> usize {
#[cfg(test)]
mod tests {
use crate::{cstr, utils::SharedThinCstr};
use crate::{cstr, utils::CStrRef};
#[test]
fn memcpy_null() {
@ -270,8 +270,8 @@ mod tests {
#[test]
fn strncmp_no_null_term() {
// Note: this is violating the safety invariant of SharedThinCstr but thats fine, we're careful.
let a = unsafe { SharedThinCstr::from_raw(b"0000".as_ptr()) };
let b = unsafe { SharedThinCstr::from_raw(b"0001".as_ptr()) };
let a = unsafe { CStrRef::from_raw(b"0000".as_ptr()) };
let b = unsafe { CStrRef::from_raw(b"0001".as_ptr()) };
let result = unsafe { super::strncmp(a, b, 4) };
assert_eq!(result, -1);
}

View file

@ -1,11 +1,11 @@
use crate::utils::SharedThinCstr;
use crate::utils::CStrRef;
use core::fmt::Write;
pub fn assert_failed(
assertion: SharedThinCstr<'_>,
file: SharedThinCstr<'_>,
assertion: CStrRef<'_>,
file: CStrRef<'_>,
line: u32,
_function: Option<SharedThinCstr<'_>>,
_function: Option<CStrRef<'_>>,
) -> ! {
let _ = writeln!(
crate::io::Printer(crate::io::STDERR),

View file

@ -1,13 +1,13 @@
use core::ffi::{c_char, c_int};
use crate::utils::SharedThinCstr;
use crate::utils::CStrRef;
/// The entrypoint of the program.
/// This is called by a bit of assembly handling architecture-specific _start.
pub(crate) unsafe extern "C" fn start(rsp: u64) -> ! {
let argc = (rsp as *const u64).read();
let argv = (rsp + 8) as *const *const c_char;
let envp = (8 + 8 * argc + rsp + 8) as *mut Option<SharedThinCstr<'static>>;
let envp = (8 + 8 * argc + rsp + 8) as *mut Option<CStrRef<'static>>;
crate::env::init(envp);

59
libuwuc/src/string.rs Normal file
View file

@ -0,0 +1,59 @@
use crate::utils::CStrRef;
pub unsafe fn strtok(str: *mut u8, delim: CStrRef<'_>, saveptr: *mut *mut u8) -> *const u8 {
if !str.is_null() {
saveptr.write(str);
}
let start = saveptr.read();
let mut end = saveptr.read();
while end.read() != 0 || !delim.into_iter().any(|c| c == end.read()) {
end = end.add(1);
}
end.write(0);
if start == end {
core::ptr::null()
} else {
start
}
}
#[cfg(test)]
mod tests {
extern crate std;
use std::borrow::ToOwned;
use std::vec::Vec;
use crate::{cstr, utils::CStrRef};
fn test_strtok(string: &str, delim: CStrRef<'_>, expected: &[CStrRef<'_>]) {
unsafe {
let mut str = string.to_owned().into_bytes();
str.push(0);
let mut saveptr = core::ptr::null_mut();
let mut out = Vec::new();
loop {
let s = super::strtok(str.as_mut_ptr(), delim, &mut saveptr);
if s.is_null() {
break;
}
out.push(CStrRef::from_raw(s));
}
assert_eq!(out, expected);
}
}
#[test]
fn strtok_manpage_example() {
test_strtok(
"a/bbb///cc;xxxx:yyy:",
cstr!(":;"),
&[cstr!("a/bbbb///cc"), cstr!("xxx"), cstr!("yyy")],
);
}
}

View file

@ -2,13 +2,13 @@ use core::{cell::UnsafeCell, ffi::CStr, fmt::Debug, marker::PhantomData, ptr::No
#[repr(transparent)]
#[derive(Default)]
pub(crate) struct SyncUnsafeCell<T>(pub(crate) UnsafeCell<T>);
pub struct SyncUnsafeCell<T>(pub UnsafeCell<T>);
unsafe impl<T: Sync> Sync for SyncUnsafeCell<T> {}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct SharedThinCstr<'a>(NonNull<u8>, PhantomData<&'a CStr>);
pub struct CStrRef<'a>(NonNull<u8>, PhantomData<&'a CStr>);
#[macro_export]
macro_rules! cstr {
@ -16,14 +16,14 @@ macro_rules! cstr {
let s = concat!($value, "\0");
#[allow(unused_unsafe)]
unsafe {
$crate::utils::SharedThinCstr::from_raw(s.as_ptr().cast())
$crate::utils::CStrRef::from_raw(s.as_ptr().cast())
}
}};
}
pub use cstr;
impl<'a> SharedThinCstr<'a> {
impl<'a> CStrRef<'a> {
pub unsafe fn from_raw(ptr: *const u8) -> Self {
Self(NonNull::new_unchecked(ptr as *mut u8), PhantomData)
}
@ -50,7 +50,7 @@ impl<'a> SharedThinCstr<'a> {
}
}
impl<'a> IntoIterator for SharedThinCstr<'a> {
impl<'a> IntoIterator for CStrRef<'a> {
type Item = u8;
type IntoIter = CStrIter<'a>;
@ -60,7 +60,7 @@ impl<'a> IntoIterator for SharedThinCstr<'a> {
}
}
pub struct CStrIter<'a>(SharedThinCstr<'a>);
pub struct CStrIter<'a>(CStrRef<'a>);
impl<'a> Iterator for CStrIter<'a> {
type Item = u8;
@ -78,10 +78,10 @@ impl<'a> Iterator for CStrIter<'a> {
}
}
unsafe impl<'a> Send for SharedThinCstr<'a> {}
unsafe impl<'a> Sync for SharedThinCstr<'a> {}
unsafe impl<'a> Send for CStrRef<'a> {}
unsafe impl<'a> Sync for CStrRef<'a> {}
impl<'a> Debug for SharedThinCstr<'a> {
impl<'a> Debug for CStrRef<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let str = <&CStr>::from(*self).to_str();
match str {
@ -91,22 +91,22 @@ impl<'a> Debug for SharedThinCstr<'a> {
}
}
impl<'a> From<SharedThinCstr<'a>> for &'a CStr {
fn from(value: SharedThinCstr<'a>) -> Self {
impl<'a> From<CStrRef<'a>> for &'a CStr {
fn from(value: CStrRef<'a>) -> Self {
unsafe { CStr::from_ptr(value.0.as_ptr().cast()) }
}
}
impl<'a> PartialEq for SharedThinCstr<'a> {
impl<'a> PartialEq for CStrRef<'a> {
fn eq(&self, other: &Self) -> bool {
self.into_iter().eq(*other)
}
}
impl<'a> Eq for SharedThinCstr<'a> {}
impl<'a> Eq for CStrRef<'a> {}
#[repr(transparent)]
pub(crate) struct SyncPtr<T>(pub(crate) *mut T);
pub struct SyncPtr<T>(pub *mut T);
unsafe impl<T> Send for SyncPtr<T> {}
unsafe impl<T> Sync for SyncPtr<T> {}

View file

@ -1,6 +1,6 @@
use libuwuc::{error::IntoOkOrErrno, utils::SharedThinCstr, io::fd::Fd};
use libuwuc::{error::IntoOkOrErrno, utils::CStrRef, io::fd::Fd};
#[no_mangle]
pub unsafe extern "C" fn open(path: SharedThinCstr<'_>, flags: i32) -> Fd {
pub unsafe extern "C" fn open(path: CStrRef<'_>, flags: i32) -> Fd {
libuwuc::io::fd::open(path, flags).into_ok_or_errno()
}

View file

@ -1,6 +1,6 @@
use core::ffi::c_uint;
use libuwuc::utils::SharedThinCstr;
use libuwuc::utils::CStrRef;
#[no_mangle]
pub extern "C" fn __stack_chk_fail() -> ! {
@ -23,9 +23,9 @@ pub unsafe extern "C" fn __assert_fail(
function: *const u8,
) -> ! {
libuwuc::misc::assert_failed(
SharedThinCstr::from_raw(assertion),
SharedThinCstr::from_raw(file),
CStrRef::from_raw(assertion),
CStrRef::from_raw(file),
line,
SharedThinCstr::from_nullable(function),
CStrRef::from_nullable(function),
)
}

View file

@ -3,7 +3,7 @@ use core::ffi::{c_char, c_int};
use libuwuc::{
error::IntoOkOrErrno,
io::{stream::FileStream, traits::WriteCounter, STDERR, STDIN, STDOUT},
utils::SharedThinCstr,
utils::CStrRef,
};
#[no_mangle]
@ -24,7 +24,7 @@ pub unsafe extern "C" fn __printf_chk(_flag: c_int, format: *const u8, mut args:
let result = libuwuc::fmt::printf::printf_generic(
&mut sink,
SharedThinCstr::from_raw(format),
CStrRef::from_raw(format),
args.as_va_list(),
);
@ -37,7 +37,7 @@ pub unsafe extern "C" fn printf(format: *const u8, mut args: ...) -> c_int {
let result = libuwuc::fmt::printf::printf_generic(
&mut sink,
SharedThinCstr::from_raw(format),
CStrRef::from_raw(format),
args.as_va_list(),
);
@ -55,7 +55,7 @@ pub unsafe extern "C" fn __fprintf_chk(
let result = libuwuc::fmt::printf::printf_generic(
&mut sink,
SharedThinCstr::from_raw(format),
CStrRef::from_raw(format),
args.as_va_list(),
);
@ -68,7 +68,7 @@ pub unsafe extern "C" fn fprintf(file: &FileStream, format: *const u8, mut args:
let result = libuwuc::fmt::printf::printf_generic(
&mut sink,
SharedThinCstr::from_raw(format),
CStrRef::from_raw(format),
args.as_va_list(),
);
@ -86,8 +86,8 @@ pub static stderr: &FileStream = &FileStream::from_raw_fd(STDERR);
#[no_mangle]
pub unsafe extern "C" fn fopen<'a>(
pathname: SharedThinCstr<'_>,
mode: SharedThinCstr<'_>,
pathname: CStrRef<'_>,
mode: CStrRef<'_>,
) -> Option<&'a FileStream> {
libuwuc::io::stream::fopen(pathname, mode)
.map_err(|err| libuwuc::error::set_errno(err.0))

View file

@ -1,6 +1,6 @@
use core::ffi::{c_int, c_long};
use libuwuc::{error::IntoOkOrErrno, utils::SharedThinCstr};
use libuwuc::{error::IntoOkOrErrno, utils::CStrRef};
// Allocation functions
@ -35,10 +35,10 @@ pub unsafe extern "C" fn reallocarray(ptr: *mut u8, nmemb: usize, size: usize) -
#[no_mangle]
pub unsafe extern "C" fn strtol(nptr: *const u8, endptr: *mut *const u8, base: c_int) -> c_long {
let str = SharedThinCstr::from_raw(nptr);
let str = CStrRef::from_raw(nptr);
libuwuc::fmt::parse::parse_long(
str,
core::mem::transmute::<*mut *const u8, Option<&mut Option<SharedThinCstr<'_>>>>(endptr),
core::mem::transmute::<*mut *const u8, Option<&mut Option<CStrRef<'_>>>>(endptr),
base,
)
.into_ok_or_errno()
@ -53,8 +53,8 @@ pub unsafe extern "C" fn strtoll(nptr: *const u8, endptr: *mut *const u8, base:
#[no_mangle]
pub unsafe extern "C" fn getenv(name: *const u8) -> *const u8 {
libuwuc::env::getenv(SharedThinCstr::from_raw(name))
.map(SharedThinCstr::as_raw)
libuwuc::env::getenv(CStrRef::from_raw(name))
.map(CStrRef::as_raw)
.unwrap_or(core::ptr::null())
}

View file

@ -1,4 +1,9 @@
use libuwuc::{error::Error, utils::SharedThinCstr};
use core::cell::UnsafeCell;
use libuwuc::{
error::Error,
utils::{CStrRef, SyncPtr, SyncUnsafeCell},
};
#[no_mangle]
pub unsafe extern "C" fn memset(ptr: *mut u8, constant: u8, len: usize) {
@ -26,13 +31,13 @@ pub unsafe extern "C" fn bcmp(s1: *const u8, s2: *const u8, size: usize) -> i32
}
#[no_mangle]
pub unsafe extern "C" fn strcmp(s1: SharedThinCstr<'_>, s2: SharedThinCstr<'_>) -> i32 {
pub unsafe extern "C" fn strcmp(s1: CStrRef<'_>, s2: CStrRef<'_>) -> i32 {
libuwuc::mem::strcmp(s1, s2)
}
// This technically violates the safety precondition of SharedThinCstr but that's fine, we're careful.
#[no_mangle]
pub unsafe extern "C" fn strncmp(s1: SharedThinCstr<'_>, s2: SharedThinCstr<'_>, n: usize) -> i32 {
pub unsafe extern "C" fn strncmp(s1: CStrRef<'_>, s2: CStrRef<'_>, n: usize) -> i32 {
libuwuc::mem::strncmp(s1, s2, n)
}
@ -47,3 +52,20 @@ pub unsafe extern "C" fn strerror(errnum: Error) -> *const u8 {
.map(str::as_ptr)
.unwrap_or(core::ptr::null())
}
static STRTOK_GLOBAL: SyncUnsafeCell<SyncPtr<u8>> =
SyncUnsafeCell(UnsafeCell::new(SyncPtr(core::ptr::null_mut())));
#[no_mangle]
pub unsafe extern "C" fn strtok(str: *mut u8, delim: *const u8) -> *const u8 {
strtok_r(str, delim, STRTOK_GLOBAL.0.get().cast::<*const u8>())
}
#[no_mangle]
pub unsafe extern "C" fn strtok_r<'a>(
str: *mut u8,
delim: *const u8,
saveptr: *mut *const u8,
) -> *const u8 {
todo!()
}