0.3.0: converge relibc to upstream 0.6.0 + Red Bear patches

This commit is contained in:
2026-07-06 19:13:08 +03:00
parent 1a0edd8eeb
commit 4ef7e57571
1466 changed files with 75236 additions and 13644 deletions
+23 -10
View File
@@ -1,8 +1,13 @@
//! `aio.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/aio.h.html>.
use crate::{
header::time::{sigevent, timespec},
platform::types::*,
header::{bits_timespec::timespec, signal::sigevent},
platform::types::{c_int, c_void},
};
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/aio.h.html>.
pub struct aiocb {
pub aio_fildes: c_int,
pub aio_lio_opcode: c_int,
@@ -12,17 +17,20 @@ pub struct aiocb {
pub aio_sigevent: sigevent,
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_read.html>.
// #[unsafe(no_mangle)]
pub extern "C" fn aio_read(aiocbp: *mut aiocb) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_write.html>.
// #[unsafe(no_mangle)]
pub extern "C" fn aio_write(aiocbp: *mut aiocb) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/lio_listio.html>.
// #[unsafe(no_mangle)]
pub extern "C" fn lio_listio(
mode: c_int,
list: *const *const aiocb,
@@ -32,22 +40,26 @@ pub extern "C" fn lio_listio(
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_error.html>.
// #[unsafe(no_mangle)]
pub extern "C" fn aio_error(aiocbp: *const aiocb) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_return.html>.
// #[unsafe(no_mangle)]
pub extern "C" fn aio_return(aiocbp: *mut aiocb) -> usize {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_cancel.html>.
// #[unsafe(no_mangle)]
pub extern "C" fn aio_cancel(fildes: c_int, aiocbp: *mut aiocb) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_suspend.html>.
// #[unsafe(no_mangle)]
pub extern "C" fn aio_suspend(
list: *const *const aiocb,
nent: c_int,
@@ -56,7 +68,8 @@ pub extern "C" fn aio_suspend(
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_fsync.html>.
// #[unsafe(no_mangle)]
pub extern "C" fn aio_fsync(operation: c_int, aiocbp: *mut aiocb) -> c_int {
unimplemented!();
}
+31 -15
View File
@@ -1,69 +1,85 @@
//! fenv.h implementation for Redox, following
//! http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/fenv.h.html
//! `fenv.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
use crate::platform::types::*;
use crate::platform::types::c_int;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
pub const FE_ALL_EXCEPT: c_int = 0;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
pub const FE_TONEAREST: c_int = 0;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
pub type fexcept_t = u64;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
#[repr(C)]
pub struct fenv_t {
pub cw: u64,
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/feclearexcept.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn feclearexcept(excepts: c_int) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub unsafe extern "C" fn fegenenv(envp: *mut fenv_t) -> c_int {
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fegetenv.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn fegetenv(envp: *mut fenv_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fegetexceptflag.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn fegetexceptflag(flagp: *mut fexcept_t, excepts: c_int) -> c_int {
unimplemented!();
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fegetround.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn fegetround() -> c_int {
FE_TONEAREST
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/feholdexcept.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn feholdexcept(envp: *mut fenv_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/feraiseexcept.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn feraiseexcept(except: c_int) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fesetenv.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn fesetenv(envp: *const fenv_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fesetexceptflag.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn fesetexceptflag(flagp: *const fexcept_t, excepts: c_int) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fegetround.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn fesetround(round: c_int) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fetestexcept.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn fetestexcept(excepts: c_int) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/feupdateenv.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn feupdateenv(envp: *const fenv_t) -> c_int {
unimplemented!();
}
+5
View File
@@ -0,0 +1,5 @@
include_guard = "_RELIBC_PATHS_H"
language = "C"
style = "Type"
no_includes = true
cpp_compat = true
+29
View File
@@ -0,0 +1,29 @@
//! Linux specific constants for paths.h.
//! Entirely borrowed from musl as there isn't a list of what to include.
pub const _PATH_DEFPATH: &str = "/usr/local/bin:/bin:/usr/bin";
pub const _PATH_STDPATH: &str = "/bin:/usr/bin:/sbin:/usr/sbin";
pub const _PATH_BSHELL: &str = "/bin/sh";
pub const _PATH_CONSOLE: &str = "/dev/console";
pub const _PATH_DEVNULL: &str = "/dev/null";
pub const _PATH_KLOG: &str = "/proc/kmsg";
pub const _PATH_LASTLOG: &str = "/var/log/lastlog";
pub const _PATH_MAILDIR: &str = "/var/mail";
pub const _PATH_MAN: &str = "/usr/share/man";
pub const _PATH_MNTTAB: &str = "/etc/fstab";
pub const _PATH_NOLOGIN: &str = "/etc/nologin";
pub const _PATH_SENDMAIL: &str = "/usr/sbin/sendmail";
pub const _PATH_SHADOW: &str = "/etc/shadow";
pub const _PATH_SHELLS: &str = "/etc/shells";
pub const _PATH_TTY: &str = "/dev/tty";
pub const _PATH_UTMP: &str = "/var/run/utmp";
pub const _PATH_WTMP: &str = "/var/log/wtmp";
pub const _PATH_VI: &str = "/usr/bin/vi";
// Trailing backslash intentional as these are dir paths.
pub const _PATH_DEV: &str = "/dev/";
pub const _PATH_TMP: &str = "/tmp/";
pub const _PATH_VARDB: &str = "/var/lib/misc/";
pub const _PATH_VARRUN: &str = "/var/run/";
pub const _PATH_VARTMP: &str = "/var/tmp/";
+11
View File
@@ -0,0 +1,11 @@
//! Implementation specific, non-standard path aliases
#[cfg(target_os = "linux")]
#[path = "linux.rs"]
mod sys;
#[cfg(target_os = "redox")]
#[path = "redox.rs"]
mod sys;
pub use sys::*;
+13
View File
@@ -0,0 +1,13 @@
//! Redox specific constants for paths.h.
// NOTE: Cross check that new entries correspond to files on Redox before adding.
pub const _PATH_BSHELL: &str = "/bin/sh";
pub const _PATH_DEVNULL: &str = "/dev/null";
pub const _PATH_MAN: &str = "/usr/share/man";
pub const _PATH_TTY: &str = "/dev/tty";
// Trailing backslash intentional as these are dir paths.
pub const _PATH_DEV: &str = "/dev/";
pub const _PATH_TMP: &str = "/tmp/";
pub const _PATH_VARTMP: &str = "/var/tmp/";
+1 -1
View File
@@ -3,7 +3,7 @@
use platform::types::*;
/*
#[no_mangle]
#[unsafe(no_mangle)]
pub extern "C" fn func(args) -> c_int {
unimplemented!();
}
-78
View File
@@ -1,78 +0,0 @@
//! wctype implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/wctype.h.html
use crate::platform::types::*;
// #[no_mangle]
pub extern "C" fn iswalnum(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswalpha(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswcntrl(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswdigit(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswgraph(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswlower(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswprint(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswpunct(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswspace(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswupper(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswxdigit(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswctype(wc: wint_t, charclass: wctype_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn towlower(wc: wint_t) -> wint_t {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn towupper(wc: wint_t) -> wint_t {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn wctype(property: *const c_char) -> c_int {
unimplemented!();
}
+4 -4
View File
@@ -1,4 +1,4 @@
use crate::platform::types::*;
use crate::platform::types::{c_double, c_uint, c_ulong, c_ulonglong};
#[repr(C)]
pub struct user_regs_struct {
@@ -16,11 +16,11 @@ pub struct user_fpsimd_struct {
}
pub type elf_greg_t = c_ulong;
pub type elf_gregset_t = [c_ulong; 34];
pub type elf_gregset_t = *mut [c_ulong; 34];
pub type elf_fpregset_t = user_fpsimd_struct;
#[no_mangle]
pub extern "C" fn _cbindgen_only_generates_structs_if_they_are_mentioned_which_is_dumb_aarch64_user(
#[unsafe(no_mangle)]
pub extern "C" fn _cbindgen_export_aarch64_user(
a: user_regs_struct,
b: user_fpsimd_struct,
c: elf_gregset_t,
@@ -0,0 +1,7 @@
sys_includes = []
include_guard = "_RISCV64_USER_H"
language = "C"
style = "Tag"
[enum]
prefix_with_name = true
+39
View File
@@ -0,0 +1,39 @@
use crate::platform::types::{c_double, c_float, c_uint, c_ulong};
#[repr(C)]
pub struct user_regs_struct {
pub regs: [c_ulong; 31], // x1-x31
pub pc: c_ulong,
}
#[repr(C)]
pub struct user_fpregs_f_struct {
pub fpregs: [c_float; 32],
pub fcsr: c_uint,
}
#[repr(C)]
pub struct user_fpregs_g_struct {
pub fpregs: [c_double; 32],
pub fcsr: c_uint,
}
#[repr(C)]
pub struct user_fpregs_struct {
pub f_regs: user_fpregs_f_struct,
pub g_regs: user_fpregs_g_struct,
}
pub type elf_greg_t = c_ulong;
pub type elf_gregset_t = user_regs_struct;
pub type elf_fpregset_t = user_fpregs_struct;
#[unsafe(no_mangle)]
pub extern "C" fn _cbindgen_only_generates_structs_if_they_are_mentioned_which_is_dumb_riscv64_user(
a: user_regs_struct,
b: user_fpregs_struct,
c: elf_gregset_t,
d: elf_greg_t,
e: elf_fpregset_t,
) {
}
+4 -4
View File
@@ -1,6 +1,6 @@
//! A part of the ptrace compatibility for Redox OS
use crate::platform::types::*;
use crate::platform::types::{c_char, c_int, c_long, c_ulong};
#[repr(C)]
pub struct user_fpregs_struct {
@@ -50,7 +50,7 @@ pub struct user_regs_struct {
pub type elf_greg_t = c_ulong;
pub type elf_gregset_t = [c_ulong; 27];
pub type elf_gregset_t = *mut [c_ulong; 27];
pub type elf_fpregset_t = user_fpregs_struct;
#[repr(C)]
pub struct user {
@@ -71,8 +71,8 @@ pub struct user {
pub u_debugreg: [c_ulong; 8],
}
#[no_mangle]
pub extern "C" fn _cbindgen_only_generates_structs_if_they_are_mentioned_which_is_dumb_x86_user(
#[unsafe(no_mangle)]
pub extern "C" fn _cbindgen_export_x86_user(
a: user_fpregs_struct,
b: user_regs_struct,
c: user,
+20 -1
View File
@@ -1,5 +1,24 @@
sys_includes = ["stddef.h", "sys/socket.h", "netinet/in.h"]
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/arpa_inet.h.html
#
# Spec quotations relating to includes:
# - "The <arpa/inet.h> header shall define the in_port_t and in_addr_t types as described in <netinet/in.h> and the socklen_t type as defined in <sys/socket.h>."
# - "The <arpa/inet.h> header shall define the in_addr structure as described in <netinet/in.h>."
# - "The <arpa/inet.h> header shall define the INET_ADDRSTRLEN and INET6_ADDRSTRLEN macros as described in <netinet/in.h>."
# - "The <arpa/inet.h> header shall define the uint32_t and uint16_t types as described in <inttypes.h>."
# - "Inclusion of the <arpa/inet.h> header may also make visible all symbols from <netinet/in.h> and <inttypes.h>."
#
# Possible cycle between arpa/inet.h and netinet/in.h solved by:
# - including netinet/in.h in arpa/inet.h
# - splitting out functions (htonl, htons, ntohl, ntohs) into bits/arpainet.h
#
# netinet/in.h brings in sys/types.h which brings in features.h (needed for #[deprecated] annotation)
# bits/arpainet.h brings in inttypes.h
sys_includes = ["netinet/in.h"]
include_guard = "_ARPA_INET_H"
after_includes = """
#include <bits/arpainet.h> // for htonl, htons, ntohl, ntohs
#include <bits/socklen-t.h> // for socklen_t
"""
language = "C"
style = "Tag"
no_includes = true
+407 -118
View File
@@ -1,5 +1,8 @@
//! arpa/inet implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xns/arpainet.h.html
//! `arpa/inet.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/arpa_inet.h.html>.
use alloc::{string::String, vec::Vec};
use core::{
ptr, slice,
str::{self, FromStr},
@@ -8,151 +11,437 @@ use core::{
use crate::{
c_str::CStr,
header::{
errno::*,
netinet_in::{in_addr, in_addr_t, INADDR_NONE},
sys_socket::{constants::*, socklen_t},
bits_arpainet::ntohl,
bits_socklen_t::socklen_t,
errno::{EAFNOSUPPORT, ENOSPC},
netinet_in::{INADDR_NONE, INET6_ADDRSTRLEN, in6_addr, in_addr, in_addr_t},
sys_socket::constants::{AF_INET, AF_INET6},
},
platform::{self, types::*},
platform::{
self,
types::{c_char, c_int, c_void},
},
raw_cell::RawCell,
};
#[no_mangle]
pub extern "C" fn htonl(hostlong: uint32_t) -> uint32_t {
hostlong.to_be()
}
#[no_mangle]
pub extern "C" fn htons(hostshort: uint16_t) -> uint16_t {
hostshort.to_be()
}
#[no_mangle]
pub extern "C" fn ntohl(netlong: uint32_t) -> uint32_t {
u32::from_be(netlong)
}
#[no_mangle]
pub extern "C" fn ntohs(netshort: uint16_t) -> uint16_t {
u16::from_be(netshort)
}
#[no_mangle]
pub unsafe extern "C" fn inet_aton(cp: *const c_char, inp: *mut in_addr) -> c_int {
// TODO: octal/hex
inet_pton(AF_INET, cp, inp as *mut c_void)
}
#[no_mangle]
pub unsafe extern "C" fn inet_ntoa(addr: in_addr) -> *const c_char {
static mut NTOA_ADDR: [c_char; 16] = [0; 16];
inet_ntop(
AF_INET,
&addr as *const in_addr as *const c_void,
NTOA_ADDR.as_mut_ptr(),
16,
)
}
#[no_mangle]
pub unsafe extern "C" fn inet_pton(domain: c_int, src: *const c_char, dest: *mut c_void) -> c_int {
if domain != AF_INET {
platform::errno = EAFNOSUPPORT;
-1
} else {
let s_addr = slice::from_raw_parts_mut(
&mut (*(dest as *mut in_addr)).s_addr as *mut _ as *mut u8,
4,
);
let src_cstr = CStr::from_ptr(src);
let mut octets = str::from_utf8_unchecked(src_cstr.to_bytes()).split('.');
for i in 0..4 {
if let Some(n) = octets.next().and_then(|x| u8::from_str(x).ok()) {
s_addr[i] = n;
} else {
return 0;
}
}
if octets.next() == None {
1 // Success
} else {
0
}
}
}
#[no_mangle]
pub unsafe extern "C" fn inet_ntop(
domain: c_int,
src: *const c_void,
dest: *mut c_char,
size: socklen_t,
) -> *const c_char {
if domain != AF_INET {
platform::errno = EAFNOSUPPORT;
ptr::null()
} else if size < 16 {
platform::errno = ENOSPC;
ptr::null()
} else {
let s_addr = slice::from_raw_parts(
&(*(src as *const in_addr)).s_addr as *const _ as *const u8,
4,
);
let addr = format!("{}.{}.{}.{}\0", s_addr[0], s_addr[1], s_addr[2], s_addr[3]);
ptr::copy(addr.as_ptr() as *const c_char, dest, addr.len());
dest
}
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/inet_addr.html>.
///
/// # Deprecated
/// The `inet_addr()` function was marked obsolescent in the Open Group Base
/// Specifications Issue 8.
#[deprecated]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn inet_addr(cp: *const c_char) -> in_addr_t {
let mut val: in_addr = in_addr { s_addr: 0 };
if inet_aton(cp, &mut val) > 0 {
if unsafe { inet_aton(cp, &raw mut val) } > 0 {
val.s_addr
} else {
INADDR_NONE
}
}
#[no_mangle]
pub extern "C" fn inet_lnaof(input: in_addr) -> in_addr_t {
if input.s_addr >> 24 < 128 {
input.s_addr & 0xff_ffff
} else if input.s_addr >> 24 < 192 {
input.s_addr & 0xffff
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/inet_aton.3.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn inet_aton(cp: *const c_char, inp: *mut in_addr) -> c_int {
let cp_cstr = unsafe { CStr::from_ptr(cp) };
let mut four_parts_decimal_only = false;
if cp_cstr.contains(b'.') {
// 2, 3 or 4 part address
let parts = unsafe { str::from_utf8_unchecked(cp_cstr.to_bytes()).split('.') };
let mut count = 0;
for part in parts {
if let Some(hex_or_oct) = part.strip_prefix('0')
&& part.len() > 1
{
match hex_or_oct.bytes().next() {
Some(b'x' | b'X') => todo_skip!(0, "parsing hex values unimplemented"),
// TODO: C2Y accept `0o` or `0O` as octal prefixes, C23 and below only use `0`
_ => todo_skip!(0, "parsing octal values unimplemented"),
}
} else {
count += 1;
}
}
if count == 4 {
four_parts_decimal_only = true;
}
} else if cp_cstr.len() == 4 {
// 1 part address (32 bit value to be stored directly into address without byte rearrangement)
let s_addr_bytes: [u8; 4] = cp_cstr.to_bytes().try_into().expect("guaranteed 4 bytes");
unsafe {
(*inp.cast::<in_addr>()).s_addr = in_addr_t::from_ne_bytes(s_addr_bytes);
}
return 1; // successful
}
if four_parts_decimal_only {
unsafe { inet_pton(AF_INET, cp, inp.cast::<c_void>()) }
} else {
input.s_addr & 0xff
todo_skip!(0, "parsing 2 or more non-decimal values unimplemented");
// TODO convert octal and hexadecimal parts into decimal and feed into `inet_pton`
0 // indicates `cp` is an invalid string
}
}
#[no_mangle]
pub extern "C" fn inet_makeaddr(net: in_addr_t, host: in_addr_t) -> in_addr {
/// See <https://pubs.opengroup.org/onlinepubs/7908799/xns/inet_lnaof.html>.
///
/// # Deprecation
/// The `inet_lnaof()` function was specified in Networking Services Issue 5,
/// but not in the Open Group Base Specifications Issue 6 and later.
#[deprecated]
#[unsafe(no_mangle)]
pub extern "C" fn inet_lnaof(r#in: in_addr) -> in_addr_t {
if r#in.s_addr >> 24 < 128 {
r#in.s_addr & 0xff_ffff
} else if r#in.s_addr >> 24 < 192 {
r#in.s_addr & 0xffff
} else {
r#in.s_addr & 0xff
}
}
/// See <https://pubs.opengroup.org/onlinepubs/7908799/xns/inet_makeaddr.html>.
///
/// # Deprecation
/// The `inet_makeaddr()` function was specified in Networking Services Issue
/// 5, but not in the Open Group Base Specifications Issue 6 and later.
#[deprecated]
#[unsafe(no_mangle)]
pub extern "C" fn inet_makeaddr(net: in_addr_t, lna: in_addr_t) -> in_addr {
let mut output: in_addr = in_addr { s_addr: 0 };
if net < 256 {
output.s_addr = host | net << 24;
output.s_addr = lna | net << 24;
} else if net < 65536 {
output.s_addr = host | net << 16;
output.s_addr = lna | net << 16;
} else {
output.s_addr = host | net << 8;
output.s_addr = lna | net << 8;
}
output
}
#[no_mangle]
pub extern "C" fn inet_netof(input: in_addr) -> in_addr_t {
if input.s_addr >> 24 < 128 {
input.s_addr & 0xff_ffff
} else if input.s_addr >> 24 < 192 {
input.s_addr & 0xffff
/// See <https://pubs.opengroup.org/onlinepubs/7908799/xns/inet_netof.html>.
///
/// # Deprecation
/// The `inet_netof()` function was specified in Networking Services Issue 5,
/// but not in the Open Group Base Specifications Issue 6 and later.
#[deprecated]
#[unsafe(no_mangle)]
pub extern "C" fn inet_netof(r#in: in_addr) -> in_addr_t {
if r#in.s_addr >> 24 < 128 {
r#in.s_addr & 0xff_ffff
} else if r#in.s_addr >> 24 < 192 {
r#in.s_addr & 0xffff
} else {
input.s_addr & 0xff
r#in.s_addr & 0xff
}
}
#[no_mangle]
pub unsafe extern "C" fn inet_network(cp: *mut c_char) -> in_addr_t {
ntohl(inet_addr(cp))
/// See <https://pubs.opengroup.org/onlinepubs/7908799/xns/inet_network.html>.
///
/// # Deprecation
/// The `inet_network()` function was specified in Networking Services Issue 5,
/// but not in the Open Group Base Specifications Issue 6 and later.
#[deprecated]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn inet_network(cp: *const c_char) -> in_addr_t {
ntohl(unsafe {
#[allow(deprecated)]
inet_addr(cp)
})
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/inet_addr.html>.
///
/// # Deprecation
/// The `inet_ntoa()` function was marked obsolescent in the Open Group Base
/// Specifications Issue 8.
#[deprecated]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn inet_ntoa(r#in: in_addr) -> *mut c_char {
static NTOA_ADDR: RawCell<[c_char; 16]> = RawCell::new([0; 16]);
unsafe {
let ptr = inet_ntop(
AF_INET,
ptr::from_ref::<in_addr>(&r#in).cast::<c_void>(),
NTOA_ADDR.unsafe_mut().as_mut_ptr(),
NTOA_ADDR.unsafe_ref().len() as socklen_t,
);
// Mutable pointer is required, inet_ntop returns destination as const pointer
ptr.cast_mut()
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/inet_ntop.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn inet_ntop(
af: c_int,
src: *const c_void,
dst: *mut c_char,
size: socklen_t,
) -> *const c_char {
if af == AF_INET6 {
if size < INET6_ADDRSTRLEN as socklen_t {
platform::ERRNO.set(ENOSPC);
return ptr::null();
}
let s6_addr = unsafe { &(*(src.cast::<in6_addr>())).s6_addr };
let output = inet_ntop6(s6_addr);
let bytes = output.as_bytes();
unsafe {
ptr::copy(bytes.as_ptr().cast::<c_char>(), dst, bytes.len());
*dst.add(bytes.len()) = 0;
}
dst
} else if af == AF_INET {
if size < 16 {
platform::ERRNO.set(ENOSPC);
ptr::null()
} else {
let s_addr = unsafe {
slice::from_raw_parts(
ptr::from_ref(&(*(src.cast::<in_addr>())).s_addr).cast::<u8>(),
4,
)
};
let addr = format!("{}.{}.{}.{}\0", s_addr[0], s_addr[1], s_addr[2], s_addr[3]);
unsafe {
ptr::copy(addr.as_ptr().cast::<c_char>(), dst, addr.len());
}
dst
}
} else {
platform::ERRNO.set(EAFNOSUPPORT);
ptr::null()
}
}
fn inet_ntop6(addr: &[u8; 16]) -> String {
let groups: [u16; 8] = core::array::from_fn(|i| {
u16::from_be_bytes([addr[i * 2], addr[i * 2 + 1]])
});
let mut best_start = 8usize;
let mut best_len = 1usize;
let mut cur_start = 8usize;
let mut cur_len = 0usize;
for i in 0..8 {
if groups[i] == 0 {
if cur_len == 0 {
cur_start = i;
}
cur_len += 1;
} else {
if cur_len > best_len {
best_start = cur_start;
best_len = cur_len;
}
cur_len = 0;
}
}
if cur_len > best_len {
best_start = cur_start;
best_len = cur_len;
}
let mut parts = Vec::new();
let mut i = 0usize;
while i < 8 {
if i == best_start && best_len > 1 {
if i == 0 {
parts.push(String::new());
}
if i + best_len == 8 {
parts.push(String::new());
}
i += best_len;
} else {
parts.push(format!("{:x}", groups[i]));
i += 1;
}
}
if best_len == 8 {
return String::from("::");
}
parts.join(":")
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/inet_ntop.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn inet_pton(af: c_int, src: *const c_char, dst: *mut c_void) -> c_int {
if af == AF_INET6 {
let src_cstr = unsafe { CStr::from_ptr(src) };
let src_str = match src_cstr.to_str() {
Ok(s) => s,
Err(_) => return 0,
};
let out = unsafe { &mut *(dst.cast::<in6_addr>()) };
if inet_pton6(src_str, &mut out.s6_addr) {
1
} else {
0
}
} else if af == AF_INET {
let s_addr = unsafe {
slice::from_raw_parts_mut(
ptr::from_mut(&mut (*dst.cast::<in_addr>()).s_addr).cast::<u8>(),
4,
)
};
let src_cstr = unsafe { CStr::from_ptr(src) };
let mut octets = unsafe { str::from_utf8_unchecked(src_cstr.to_bytes()).split('.') };
for part in s_addr.iter_mut().take(4) {
if let Some(n) = octets
.next()
.filter(|x| !x.len() > 3)
.and_then(|x| u8::from_str(x).ok())
{
*part = n;
} else {
return 0;
}
}
if octets.next().is_none() {
1 // Success
} else {
0
}
} else {
platform::ERRNO.set(EAFNOSUPPORT);
-1
}
}
fn inet_pton6(src: &str, dst: &mut [u8; 16]) -> bool {
dst.fill(0);
let double_colon_pos = src.find("::");
let second_double = if let Some(pos) = double_colon_pos {
src[pos + 2..].find("::").map(|p| p + pos + 2)
} else {
None
};
if second_double.is_some() {
return false;
}
let (left_str, right_str) = match double_colon_pos {
Some(pos) => (&src[..pos], &src[pos + 2..]),
None => (src, ""),
};
let left_groups: Vec<&str> = if left_str.is_empty() {
Vec::new()
} else {
left_str.split(':').collect()
};
let right_groups: Vec<&str> = if right_str.is_empty() {
Vec::new()
} else {
right_str.split(':').collect()
};
let right_has_ipv4 = right_groups.last().is_some_and(|g| g.contains('.'));
let mut left_count = left_groups.len();
let mut right_count = right_groups.len();
if right_has_ipv4 {
right_count -= 1;
left_count += 1;
}
let gap = 8 - left_count - right_count;
if double_colon_pos.is_none() && gap != 0 {
return false;
}
if double_colon_pos.is_some() && gap < 0 {
return false;
}
if double_colon_pos.is_none() && left_groups.len() + right_groups.len() != 8 {
return false;
}
let mut idx = 0usize;
for group in &left_groups {
if idx >= 16 {
return false;
}
let val = match parse_hex_group(group) {
Some(v) => v,
None => return false,
};
dst[idx] = (val >> 8) as u8;
dst[idx + 1] = val as u8;
idx += 2;
}
if double_colon_pos.is_some() {
for _ in 0..gap {
if idx >= 16 {
return false;
}
dst[idx] = 0;
dst[idx + 1] = 0;
idx += 2;
}
}
let right_hex_count = if right_has_ipv4 {
right_groups.len().saturating_sub(1)
} else {
right_groups.len()
};
for group in &right_groups[..right_hex_count] {
if idx >= 16 {
return false;
}
let val = match parse_hex_group(group) {
Some(v) => v,
None => return false,
};
dst[idx] = (val >> 8) as u8;
dst[idx + 1] = val as u8;
idx += 2;
}
if right_has_ipv4 {
if idx != 12 {
return false;
}
let ipv4_str = right_groups[right_groups.len() - 1];
let parts: Vec<&str> = ipv4_str.split('.').collect();
if parts.len() != 4 {
return false;
}
for (i, part) in parts.iter().enumerate() {
match u8::from_str(part) {
Ok(v) => dst[12 + i] = v,
Err(_) => return false,
}
}
idx += 4;
}
idx == 16
}
fn parse_hex_group(s: &str) -> Option<u16> {
if s.is_empty() || s.len() > 4 {
return None;
}
let mut val: u16 = 0;
for c in s.chars() {
val = val.checked_mul(16)?;
match c.to_digit(16) {
Some(d) => val = val.checked_add(d as u16)?,
None => return None,
}
}
Some(val)
}
+20 -1
View File
@@ -1,5 +1,24 @@
sys_includes = ["bits/assert.h"]
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/assert.h.html
#
# There are no spec quotations relating to includes
#
# features.h included for Rust never type (no return)
sys_includes = ["features.h"]
include_guard = "_RELIBC_ASSERT_H"
# trailer is placed outside include_guard
trailer = """
// Do not use include guard, to ensure assert is always defined
#ifdef assert
#undef assert
#endif
#ifdef NDEBUG
# define assert(cond) (void) 0
#else
# define assert(cond) \
((void)((cond) || (__assert_fail(__func__, __FILE__, __LINE__, #cond), 0)))
#endif
"""
language = "C"
style = "Tag"
no_includes = true
+12 -19
View File
@@ -1,31 +1,24 @@
//! assert implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/assert.h.html
//! `assert.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/assert.h.html>.
use crate::{
c_str::CStr,
header::{stdio, stdlib},
platform::types::*,
platform::types::{c_char, c_int},
};
use core::fmt::Write;
#[no_mangle]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __assert_fail(
func: *const c_char,
file: *const c_char,
line: c_int,
cond: *const c_char,
) {
let func = CStr::from_ptr(func).to_str().unwrap();
let file = CStr::from_ptr(file).to_str().unwrap();
let cond = CStr::from_ptr(cond).to_str().unwrap();
) -> ! {
let func = unsafe { CStr::from_ptr(func) }.to_string_lossy();
let file = unsafe { CStr::from_ptr(file) }.to_string_lossy();
let cond = unsafe { CStr::from_ptr(cond) }.to_string_lossy();
writeln!(
*stdio::stderr,
"{}: {}:{}: Assertion `{}` failed.",
func,
file,
line,
cond
)
.unwrap();
stdlib::abort();
eprintln!("{}: {}:{}: Assertion `{}` failed.", func, file, line, cond);
unsafe { crate::header::stdlib::abort() };
}
+19
View File
@@ -0,0 +1,19 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/arpa_inet.h.html
#
# The arpa/inet header should define the following functions:
# - htonl
# - htons
# - ntohl
# - ntohs
#
# They are also meant to be available in the netinet/in header.
# The arpa/inet and netinet/in headers both say that they may include each other which creates a cycle.
# To break the cycle we include netinet/in in the arpa/inet header and split out the above functions.
#
# include inttypes.h to get uint16_t and uint32_t
sys_includes = ["inttypes.h"]
include_guard = "_RELIBC_BITS_ARPAINET_H"
language = "C"
style = "Tag"
no_includes = true
cpp_compat = true
+25
View File
@@ -0,0 +1,25 @@
use crate::platform::types::{uint16_t, uint32_t};
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/htonl.html>.
#[unsafe(no_mangle)]
pub extern "C" fn htonl(hostlong: uint32_t) -> uint32_t {
hostlong.to_be()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/htonl.html>.
#[unsafe(no_mangle)]
pub extern "C" fn htons(hostshort: uint16_t) -> uint16_t {
hostshort.to_be()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/htonl.html>.
#[unsafe(no_mangle)]
pub extern "C" fn ntohl(netlong: uint32_t) -> uint32_t {
u32::from_be(netlong)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/htonl.html>.
#[unsafe(no_mangle)]
pub extern "C" fn ntohs(netshort: uint16_t) -> uint16_t {
u16::from_be(netshort)
}
+4
View File
@@ -0,0 +1,4 @@
//! `bits/eventfd.h` — eventfd counter type.
use crate::platform::types::uint64_t;
pub type eventfd_t = uint64_t;
+18
View File
@@ -0,0 +1,18 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_uio.h.html
#
# Split out to avoid including all of sys/uio.h in sys/socket.h.
#
# POSIX headers that require iovec:
# - sys/socket.h
# - sys/uio.h (where it should be defined)
#
# sys/types.h included for c_void and size_t
sys_includes = ["sys/types.h"]
include_guard = "_RELIBC_BITS_IOVEC_H"
language = "C"
style = "Tag"
no_includes = true
cpp_compat = true
[enum]
prefix_with_name = true
+38
View File
@@ -0,0 +1,38 @@
use alloc::vec::Vec;
use core::slice;
use crate::platform::types::{c_void, size_t};
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_uio.h.html>.
#[repr(C)]
#[derive(Debug, CheckVsLibcCrate)]
pub struct iovec {
pub iov_base: *mut c_void,
pub iov_len: size_t,
}
impl iovec {
unsafe fn to_slice(&self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.iov_base.cast::<u8>(), self.iov_len) }
}
}
pub unsafe fn gather(iovs: &[iovec]) -> Vec<u8> {
let mut vec = Vec::new();
for iov in iovs.iter() {
vec.extend_from_slice(unsafe { iov.to_slice() });
}
vec
}
pub unsafe fn scatter(iovs: &[iovec], vec: Vec<u8>) {
let mut i = 0;
for iov in iovs.iter() {
let slice = unsafe { iov.to_slice() };
slice.copy_from_slice(&vec[i..][..slice.len()]);
i += slice.len();
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cbindgen_alias_iovec(_: iovec) {}
+29
View File
@@ -0,0 +1,29 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/locale.h.html
#
# The locale header should define this type but multiple headers make use of it.
# This type is split out from the locale header to avoid including all of locale in the other headers.
#
# POSIX headers that require locale_t:
# - ctype.h
# - langinfo.h
# - libintl.h (Redox does not have this header, it seems to be supplied externally)
# - monetary.h (TODO note exists, not currently imported)
# - string.h
# - strings.h (TODO note exists, not currently imported)
# - time.h (TODO note exists, not currently imported)
# - wchar.h (required by *_l functions, no TODO note present)
# - wctype.h (required by *_l functions, TODO note exists, not currently imported)
sys_includes = []
include_guard = "_RELIBC_BITS_LOCALE_T_H"
language = "C"
style = "type"
no_includes = true
cpp_compat = true
[export]
include = [
"locale_t"
]
[enum]
prefix_with_name = true
+1
View File
@@ -0,0 +1 @@
pub type locale_t = *mut core::ffi::c_void;
+20
View File
@@ -0,0 +1,20 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stddef.h.html
#
# This type is split out to prevent importing all of stddef.h into other headers.
include_guard = "_RELIBC_BITS_NULL_T_H"
language = "C"
no_includes = true
after_includes = """
// Null pointer constant.
// Any pointer object whose representation has all bits set to zero, perhaps by
// memset() to 0 or by calloc(), shall be treated as a null pointer.
#ifdef __cplusplus
#if __cplusplus >= 201103L
#define NULL nullptr
#else
#define NULL 0L
#endif
#else
#define NULL ((void *)0)
#endif
"""
+5
View File
@@ -0,0 +1,5 @@
//! `NULL` from `stddef.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stddef.h.html>.
//!
//! Implemented via ifdefs and defines in cbindgen.
+53
View File
@@ -0,0 +1,53 @@
sys_includes = []
include_guard = "_RELIBC_BITS_PTHREAD_H"
language = "C"
style = "type"
no_includes = true
cpp_compat = true
# TODO: Any better way to implement pthread_cleanup_push/pthread_cleanup_pop?
after_includes = """
#define PTHREAD_COND_INITIALIZER ((pthread_cond_t){0})
#define PTHREAD_MUTEX_INITIALIZER ((pthread_mutex_t){0})
#define PTHREAD_ONCE_INIT ((pthread_once_t){0})
#define PTHREAD_RWLOCK_INITIALIZER ((pthread_rwlock_t){0})
#define pthread_cleanup_push(ROUTINE, ARG) do { \\
struct { \\
void (*routine)(void *); \\
void *arg; \\
void *prev; \\
} __relibc_internal_pthread_ll_entry = { \\
.routine = (void (*)(void *))(ROUTINE), \\
.arg = (void *)(ARG), \\
}; \\
__relibc_internal_pthread_cleanup_push(&__relibc_internal_pthread_ll_entry);
#define pthread_cleanup_pop(EXECUTE) \\
__relibc_internal_pthread_cleanup_pop((EXECUTE)); \\
} while(0)
"""
[export.rename]
"AtomicInt" = "int"
"AtomicUint" = "unsigned"
[export]
include = [
"pthread_attr_t",
"pthread_rwlockattr_t",
"pthread_rwlock_t",
"pthread_barrier_t",
"pthread_barrierattr_t",
"pthread_mutex_t",
"pthread_mutexattr_t",
"pthread_condattr_t",
"pthread_cond_t",
"pthread_spinlock_t",
"pthread_once_t",
"pthread_t",
"pthread_key_t",
]
[enum]
prefix_with_name = true
+123
View File
@@ -0,0 +1,123 @@
#![allow(non_camel_case_types)]
use crate::platform::types::{c_int, c_uchar, c_ulong, c_void, size_t};
// XXX: https://github.com/eqrion/cbindgen/issues/685
//
// We need to write the opaque types ourselves, and apparently cbindgen doesn't even support
// expanding macros! Instead, we rely on checking that the lengths are correct, when these headers
// are parsed in the regular compilation phase.
/// The `pthread_attr_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[repr(C)]
pub union pthread_attr_t {
__relibc_internal_size: [c_uchar; 32],
__relibc_internal_align: size_t,
}
/// The `pthread_rwlockattr_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[repr(C)]
pub union pthread_rwlockattr_t {
__relibc_internal_size: [c_uchar; 1],
__relibc_internal_align: c_uchar,
}
/// The `pthread_rwlock_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[repr(C)]
pub union pthread_rwlock_t {
__relibc_internal_size: [c_uchar; 4],
__relibc_internal_align: c_int,
}
/// The `pthread_barrier_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[repr(C)]
pub union pthread_barrier_t {
__relibc_internal_size: [c_uchar; 24],
__relibc_internal_align: c_int,
}
/// The `pthread_barrierattr_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[repr(C)]
pub union pthread_barrierattr_t {
__relibc_internal_size: [c_uchar; 4],
__relibc_internal_align: c_int,
}
/// The `pthread_mutex_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[repr(C)]
pub union pthread_mutex_t {
__relibc_internal_size: [c_uchar; 12],
__relibc_internal_align: c_int,
}
/// The `pthread_mutexattr_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[repr(C)]
pub union pthread_mutexattr_t {
__relibc_internal_size: [c_uchar; 20],
__relibc_internal_align: c_int,
}
/// The `pthread_cond_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[repr(C)]
pub union pthread_cond_t {
__relibc_internal_size: [c_uchar; 8],
__relibc_internal_align: c_int,
}
/// The `pthread_condattr_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[repr(C)]
pub union pthread_condattr_t {
__relibc_internal_size: [c_uchar; 8],
__relibc_internal_align: c_int,
}
/// The `pthread_spinlock_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[repr(C)]
pub union pthread_spinlock_t {
__relibc_internal_size: [c_uchar; 4],
__relibc_internal_align: c_int,
}
/// The `pthread_once_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[repr(C)]
pub union pthread_once_t {
__relibc_internal_size: [c_uchar; 4],
__relibc_internal_align: c_int,
}
macro_rules! assert_equal_size(
($export:ident, $wrapped:ident) => {
const _: () = unsafe {
type Wrapped = crate::header::pthread::$wrapped;
// Fail at compile-time if sizes differ.
// TODO: Is this UB?
let export = $export { __relibc_internal_align: 0 };
let _: Wrapped = core::mem::transmute(export.__relibc_internal_size);
// Fail at compile-time if alignments differ.
let a = [0_u8; core::mem::align_of::<$export>()];
#[allow(clippy::useless_transmute)]
let b: [u8; core::mem::align_of::<Wrapped>()] = core::mem::transmute(a);
};
// TODO: Turn into a macro?
#[cfg(all(target_os = "redox", feature = "check_against_libc_crate"))]
const _: () = unsafe {
use ::__libc_only_for_layout_checks as libc;
let export = $export { __relibc_internal_align: 0 };
let _: libc::$export = core::mem::transmute(export.__relibc_internal_size);
let a = [0_u8; core::mem::align_of::<$export>()];
let b: [u8; core::mem::align_of::<libc::$export>()] = core::mem::transmute(a);
};
}
);
assert_equal_size!(pthread_attr_t, RlctAttr);
assert_equal_size!(pthread_rwlock_t, RlctRwlock);
assert_equal_size!(pthread_rwlockattr_t, RlctRwlockAttr);
assert_equal_size!(pthread_barrier_t, RlctBarrier);
assert_equal_size!(pthread_barrierattr_t, RlctBarrierAttr);
assert_equal_size!(pthread_mutex_t, RlctMutex);
assert_equal_size!(pthread_mutexattr_t, RlctMutexAttr);
assert_equal_size!(pthread_cond_t, RlctCond);
assert_equal_size!(pthread_condattr_t, RlctCondAttr);
assert_equal_size!(pthread_spinlock_t, RlctSpinlock);
assert_equal_size!(pthread_once_t, RlctOnce);
/// The `pthread_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type pthread_t = *mut c_void;
/// The `pthread_key_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type pthread_key_t = c_ulong;
+21
View File
@@ -0,0 +1,21 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_socket.h.html
#
# The sys/socket header should define this type.
# It is split out to avoid other headers from including all of sys/socket.
#
# POSIX headers that require sa_family_t:
# - netinet/in.h
# - sys/socket.h
# - sys/un.h
sys_includes = []
include_guard = "_RELIBC_BITS_SA_FAMILY_T_H"
language = "C"
style = "type"
no_includes = true
cpp_compat = true
[export]
include = ["sa_family_t"]
[enum]
prefix_with_name = true
+3
View File
@@ -0,0 +1,3 @@
use crate::platform::types::c_ushort;
pub type sa_family_t = c_ushort;
+18
View File
@@ -0,0 +1,18 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html
#
# This type is split out to prevent importing all of signal.h into other headers.
#
# POSIX headers that require sigset_t:
# - poll.h
# - signal.h (where it should be defined)
# - sys/select.h
sys_includes = []
include_guard = "_RELIBC_BITS_SIGSET_T_H"
language = "C"
style = "type"
no_includes = true
cpp_compat = true
[export]
include = ["sigset_t"]
[enum]
prefix_with_name = true
+4
View File
@@ -0,0 +1,4 @@
use crate::platform::types::c_ulonglong;
#[allow(non_camel_case_types)]
pub type sigset_t = c_ulonglong;
+30
View File
@@ -0,0 +1,30 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stddef.h.html
#
# This type is split out to prevent importing all of stddef.h into other headers.
#
# C++ compatibility: in C++, `size_t` is defined by the standard library
# in namespace `std::`. A bare `typedef unsigned long size_t;` at file
# scope in a C++ translation unit collides with `std::size_t` whenever
# a host stdlib header (e.g. `<string>`, `<bits/basic_string.h>`) is
# transitively included — the libstdc++ internal code references
# unqualified `size_t` and resolves it to the first visible one, which
# would otherwise be this typedef. The `__cplusplus` guard makes the
# typedef a no-op under C++ compilation; the libc++/libstdc++ headers
# then provide their own `std::size_t` via `__SIZE_TYPE__` or the
# standard type machinery.
include_guard = "_RELIBC_BITS_SIZE_T_H"
after_includes = """
/**
* Unsigned integer type of the result of the sizeof operator.
*/
#ifndef __cplusplus
typedef unsigned long size_t;
#endif
"""
language = "C"
no_includes = true
cpp_compat = true
[export]
include = ["size_t"]
[enum]
prefix_with_name = true
+9
View File
@@ -0,0 +1,9 @@
//! `size_t` from `stddef.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stddef.h.html>.
use crate::platform::types::c_ulong;
/// Unsigned integer type of the result of the sizeof operator.
#[allow(non_camel_case_types)]
pub type size_t = c_ulong;
+12
View File
@@ -0,0 +1,12 @@
sys_includes = []
include_guard = "_RELIBC_BITS_SOCKLEN_T_H"
language = "C"
style = "type"
no_includes = true
cpp_compat = true
[export]
include = [
"socklen_t"
]
[enum]
prefix_with_name = true
+1
View File
@@ -0,0 +1 @@
pub type socklen_t = u32;
+25
View File
@@ -0,0 +1,25 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/time.h.html
#
# The time header should define timespec but multiple headers make use of it.
# This type is split out from the time header to avoid including all of time in the other headers.
#
# POSIX headers that require timespec:
# - aio.h (all functions currently unimplemented but timespec imported)
# - poll.h
# - sched.h
# - semaphore.h
# - signal.h
# - sys/select.h (not currently imported, needed for pselect, no TODO)
# - sys/stat.h
# - sys/time.h
#
# include sys/types.h to get time_t for timespec
sys_includes = ["sys/types.h"]
include_guard = "_RELIBC_BITS_TIME_H"
language = "C"
style = "Tag"
no_includes = true
cpp_compat = true
[enum]
prefix_with_name = true
+64
View File
@@ -0,0 +1,64 @@
use crate::{
header::time::NANOSECONDS,
platform::types::{c_long, time_t},
};
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/time.h.html>.
#[repr(C)]
#[derive(Clone, Default, Debug)]
pub struct timespec {
pub tv_sec: time_t,
pub tv_nsec: c_long,
}
impl timespec {
// TODO: Write test
/// similar logic with timeradd
#[allow(clippy::should_implement_trait)] // not to confuse std::ops::Add
pub fn add(base: timespec, interval: timespec) -> Option<timespec> {
let delta_sec = base.tv_sec.checked_add(interval.tv_sec)?;
let delta_nsec = base.tv_nsec.checked_add(interval.tv_nsec)?;
if delta_sec < 0 || delta_nsec < 0 {
return None;
}
Some(Self {
tv_sec: delta_sec + (delta_nsec / NANOSECONDS) as time_t,
tv_nsec: delta_nsec % NANOSECONDS,
})
}
/// similar logic with timersub
pub fn subtract(later: timespec, earlier: timespec) -> Option<timespec> {
let delta_sec = later.tv_sec.checked_sub(earlier.tv_sec)?;
let delta_nsec = later.tv_nsec.checked_sub(earlier.tv_nsec)?;
let time = if delta_nsec < 0 {
let roundup_sec = -delta_nsec / NANOSECONDS + 1;
timespec {
tv_sec: delta_sec - (roundup_sec as time_t),
tv_nsec: roundup_sec * NANOSECONDS - delta_nsec,
}
} else {
timespec {
tv_sec: delta_sec + (delta_nsec / NANOSECONDS) as time_t,
tv_nsec: delta_nsec % NANOSECONDS,
}
};
if time.tv_sec < 0 {
// https://man7.org/linux/man-pages/man2/settimeofday.2.html
// caller should return EINVAL
return None;
}
Some(time)
}
pub fn is_default(&self) -> bool {
self.tv_nsec == 0 && self.tv_sec == 0
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cbindgen_stupid_alias_timespec(_: timespec) {}
+21
View File
@@ -0,0 +1,21 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stddef.h.html
#
# This type is split out to prevent importing all of stddef.h into inttypes.h
include_guard = "_RELIBC_BITS_WCHAR_T_H"
after_includes = """
// Integer type whose range of values can represent distinct codes for all
// members of the largest extended character set specified among the supported
// locales; the null character shall have the code value zero.
#ifndef _WCHAR_T
#define _WCHAR_T
#ifndef __cplusplus
#ifndef __WCHAR_TYPE__
#define __WCHAR_TYPE__ int
#endif
typedef __WCHAR_TYPE__ wchar_t;
#endif // __cplusplus
#endif // _WCHAR_T
"""
language = "C"
no_includes = true
cpp_compat = true
+5
View File
@@ -0,0 +1,5 @@
//! `wchar_t` from `stddef.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stddef.h.html>.
//!
//! Implemented via ifdefs and defines in cbindgen.
+16
View File
@@ -0,0 +1,16 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/cpio.h.html
#
# There are no spec quotations relating to includes
sys_includes = []
include_guard = "_RELIBC_CPIO_H"
# cbindgen still can't handle exporting CStr
after_includes = """
#define MAGIC "070707"
"""
language = "C"
style = "Type"
no_includes = true
cpp_compat = true
[enum]
prefix_with_name = true
+48
View File
@@ -0,0 +1,48 @@
//! `cpio.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/cpio.h.html>.
use crate::platform::types::c_int;
/// Read by owner.
pub const C_IRUSR: c_int = 0o0000400;
/// Write by owner.
pub const C_IWUSR: c_int = 0o0000200;
/// Execute by owner.
pub const C_IXUSR: c_int = 0o0000100;
/// Read by group.
pub const C_IRGRP: c_int = 0o0000040;
/// Write by group.
pub const C_IWGRP: c_int = 0o0000020;
/// Execute by group.
pub const C_IXGRP: c_int = 0o0000010;
/// Read by others.
pub const C_IROTH: c_int = 0o0000004;
/// Write by others.
pub const C_IWOTH: c_int = 0o0000002;
/// Execute by others.
pub const C_IXOTH: c_int = 0o0000001;
/// Set user ID.
pub const C_ISUID: c_int = 0o0004000;
/// Set group ID.
pub const C_ISGID: c_int = 0o0002000;
/// On directories, restricted deletion flag.
pub const C_ISVTX: c_int = 0o0001000;
/// Directory.
pub const C_ISDIR: c_int = 0o0040000;
/// FIFO.
pub const C_ISFIFO: c_int = 0o0010000;
/// Regular file.
pub const C_ISREG: c_int = 0o0100000;
/// Block special.
pub const C_ISBLK: c_int = 0o0060000;
/// Character special.
pub const C_ISCHR: c_int = 0o0020000;
/// Reserved.
pub const C_ISCTG: c_int = 0o0110000;
/// Symbolic link.
pub const C_ISLNK: c_int = 0o0120000;
/// Socket.
pub const C_ISSOCK: c_int = 0o0140000;
+16
View File
@@ -0,0 +1,16 @@
use alloc::string::{String, ToString};
use argon2::{
Argon2,
password_hash::{PasswordHash, PasswordVerifier},
};
pub fn crypt_argon2(key: &str, setting: &str) -> Option<String> {
let hash = PasswordHash::new(setting).ok()?;
let argon2 = Argon2::default();
if argon2.verify_password(key.as_bytes(), &hash).is_ok() {
Some(setting.to_string())
} else {
None
}
}
+90
View File
@@ -0,0 +1,90 @@
use crate::platform::types::{c_uchar, c_uint};
use alloc::{string::String, vec::Vec};
use base64ct::{Base64Bcrypt, Encoding};
use bcrypt_pbkdf::bcrypt_pbkdf;
use core::str;
const MIN_COST: u32 = 4;
const MAX_COST: u32 = 31;
const BHASH_WORDS: usize = 8;
const BHASH_OUTPUT_SIZE: usize = BHASH_WORDS * 4;
/// Inspired by https://github.com/Keats/rust-bcrypt/blob/87fc59e917bcb6cf3f3752fc7f2b4c659d415597/src/lib.rs#L135
fn split_with_prefix(hash: &str) -> Option<(&str, &str, c_uint)> {
let valid_prefixes = ["2y", "2b", "2a", "2x"];
// Should be [prefix, cost, hash]
let raw_parts: Vec<_> = hash.split('$').skip(1).collect();
if raw_parts.len() != 3 {
return None;
}
let prefix = raw_parts[0];
let setting = raw_parts[2];
if !valid_prefixes.contains(&prefix) {
return None;
}
raw_parts[1]
.parse::<c_uint>()
.ok()
.map(|cost| (prefix, setting, cost))
}
/// Performs Blowfish key derivation on a given password with a specific setting.
///
/// # Parameters
/// * `passw`: The password to be hashed. It must be a string slice (`&str`).
/// * `setting`: The settings for the Blowfish key derivation. It must be a string slice (`&str`)
/// and should follow the format `$<prefix>$<cost>$<setting>`, where `<prefix>` is a string that
/// indicates the type of the hash (e.g., "$2a$"), `<cost>` is a decimal number representing
/// the cost factor for the Blowfish operation, and `<setting>` is a base64-encoded string
/// representing the salt to be used for the Blowfish function.
///
/// # Returns
/// * `Option<String>`: Returns `Some(String)` if the Blowfish operation was successful, where the
/// returned string is the result of the Blowfish operation formatted according to the Modular
/// Crypt Format (MCF). If the Blowfish operation failed, it returns `None`.
///
/// # Errors
/// * If the cost factor is outside the range `[MIN_COST, MAX_COST]`.
///
/// # Example
/// ```
/// let password = "correctbatteryhorsestapler";
/// let setting = "$2y$12$L6Bc/AlTQHyd9liGgGEZyO";
/// let result = crypt_blowfish(password, setting);
/// assert!(result.is_some());
///```
///
/// # Note
/// The `crypt_blowfish` function uses the Blowfish block cipher for hashing.
/// The output of the Blowfish operation is base64-encoded using the BCrypt variant of base64.
pub fn crypt_blowfish(passw: &str, setting: &str) -> Option<String> {
if let Some((prefix, setting, cost)) = split_with_prefix(setting) {
if !(MIN_COST..=MAX_COST).contains(&cost) {
return None;
}
// Passwords need to be null terminated
let mut vec = Vec::with_capacity(passw.len() + 1);
vec.extend_from_slice(passw.as_bytes());
vec.push(0);
// We only consider the first 72 chars; truncate if necessary.
let passw_t = if vec.len() > 72 { &vec[..72] } else { &vec };
let passw: &[c_uchar] = passw_t;
let setting = setting.as_bytes();
let mut output = vec![0; BHASH_OUTPUT_SIZE + 1];
bcrypt_pbkdf(passw, setting, cost, &mut output).ok()?;
Some(format!(
"${}${}${}",
prefix,
cost,
Base64Bcrypt::encode_string(&output),
))
} else {
None
}
}
+9
View File
@@ -0,0 +1,9 @@
include_guard = "_RELIBC_CRYPT_H"
language = "C"
style = "Type"
no_includes = true
cpp_compat = true
[enum]
prefix_with_name = true
+146
View File
@@ -0,0 +1,146 @@
use crate::platform::types::c_uchar;
use alloc::string::String;
use base64ct::{Base64ShaCrypt, Encoding};
use core::str;
use md5_crypto::{Digest, Md5};
// Block size for MD5
const BLOCK_SIZE: usize = 16;
// PWD part length of the password string
const PW_SIZE_MD5: usize = 22;
// Maximum length of a setting
const SALT_MAX: usize = 8;
// Inverse encoding map for MD5.
const MAP_MD5: [c_uchar; BLOCK_SIZE] = [12, 6, 0, 13, 7, 1, 14, 8, 2, 15, 9, 3, 5, 10, 4, 11];
const KEY_MAX: usize = 30000;
fn encode_md5(source: &[c_uchar]) -> Option<[c_uchar; PW_SIZE_MD5]> {
let mut transposed = [0; BLOCK_SIZE];
for (i, &ti) in MAP_MD5.iter().enumerate() {
transposed[i] = source[ti as usize];
}
let mut buf = [0; PW_SIZE_MD5];
Base64ShaCrypt::encode(&transposed, &mut buf).ok()?;
Some(buf)
}
/// Function taken from PR: https://github.com/RustCrypto/password-hashes/pull/351
/// This won't be needed once the PR is merged
fn inner_md5(passw: &str, setting: &str) -> Option<String> {
let mut digest_b = Md5::default();
digest_b.update(passw);
digest_b.update(setting);
digest_b.update(passw);
let hash_b = digest_b.finalize();
let mut digest_a = Md5::default();
digest_a.update(passw);
digest_a.update("$1$");
digest_a.update(setting);
let mut pw_len = passw.len();
let rounds = pw_len / BLOCK_SIZE;
for _ in 0..rounds {
digest_a.update(hash_b);
}
// leftover passw
digest_a.update(&hash_b[..(pw_len - rounds * BLOCK_SIZE)]);
while pw_len > 0 {
match pw_len & 1 {
0 => digest_a.update(&passw[..1]),
1 => digest_a.update([0u8]),
_ => unreachable!(),
}
pw_len >>= 1;
}
let mut hash_a = digest_a.finalize();
// Repeatedly run the collected hash value through MD5 to burn
// CPU cycles
for i in 0..1000_usize {
// new hasher
let mut hasher = Md5::default();
// Add key or last result
if (i & 1) != 0 {
hasher.update(passw);
} else {
hasher.update(hash_a);
}
// Add setting for numbers not divisible by 3
if i % 3 != 0 {
hasher.update(setting);
}
// Add key for numbers not divisible by 7
if i % 7 != 0 {
hasher.update(passw);
}
// Add key or last result
if (i & 1) != 0 {
hasher.update(hash_a);
} else {
hasher.update(passw);
}
// digest_c.clone_from_slice(&hasher.finalize());
hash_a = hasher.finalize();
}
encode_md5(hash_a.as_slice())
.map(|encstr| format!("$1${}${}", setting, str::from_utf8(&encstr).unwrap()))
}
/// Performs MD5 hashing on a given password with a specific setting.
///
/// # Parameters
/// * `passw`: The password to be hashed. It must be a string slice (`&str`).
/// * `setting`: The settings for the MD5 hashing. It must be a string slice (`&str`)
/// and should start with "$1$". The rest of the string should represent the salt
/// to be used for the MD5 hashing.
///
/// # Returns
/// * `Option<String>`: Returns `Some(String)` if the MD5 operation was successful, where the
/// returned string is the result of the MD5 operation formatted according to the Modular
/// Crypt Format (MCF). If the MD5 operation failed, it returns `None`.
///
/// # Errors
/// * If the `passw` length exceeds `KEY_MAX`.
/// * If the `setting` does not start with "$1$".
///
/// # Example
/// ```
/// let password = "my_password";
/// let setting = "$1$saltstring";
/// let result = crypt_md5(password, setting);
/// assert!(result.is_some());
/// ```
///
/// # Note
/// The `crypt_md5` function uses the MD5 hashing algorithm for hashing.
/// The output of the MD5 operation is base64-encoded using the BCrypt variant of base64.
pub fn crypt_md5(passw: &str, setting: &str) -> Option<String> {
/* reject large keys */
if passw.len() > KEY_MAX {
return None;
}
if &setting[0..3] != "$1$" {
return None;
}
let cursor = 3;
let slen = cursor
+ setting[cursor..cursor + SALT_MAX]
.chars()
.take_while(|c| *c != '$')
.count();
let setting = &setting[cursor..slen];
inner_md5(passw, setting)
}
+121
View File
@@ -0,0 +1,121 @@
//! `crypt.h` implementation.
//!
//! Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/crypt.3.html>.
use ::scrypt::password_hash::{Salt, SaltString};
use alloc::{
ffi::CString,
string::{String, ToString},
};
use core::ptr;
use rand::{Rng, SeedableRng, rngs::SmallRng};
use crate::{
c_str::CStr,
header::{errno::EINVAL, stdlib::rand},
platform::{
self,
types::{c_char, c_int},
},
};
mod argon2;
mod blowfish;
mod md5;
mod pbkdf2;
mod scrypt;
mod sha;
use self::{
argon2::crypt_argon2,
blowfish::crypt_blowfish,
md5::crypt_md5,
pbkdf2::crypt_pbkdf2,
scrypt::crypt_scrypt,
sha::{
ShaType::{Sha256, Sha512},
crypt_sha,
},
};
/// See <https://www.man7.org/linux/man-pages/man3/crypt.3.html>.
#[repr(C)]
pub struct crypt_data {
initialized: c_int,
buff: [c_char; 256],
}
impl crypt_data {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
crypt_data {
initialized: 1,
buff: [0; 256],
}
}
}
fn gen_salt() -> Option<String> {
let mut rng = SmallRng::seed_from_u64(unsafe { rand() as u64 });
let mut bytes = [0u8; Salt::RECOMMENDED_LENGTH];
rng.fill_bytes(&mut bytes);
Some(SaltString::encode_b64(&bytes).ok()?.as_str().to_string())
}
/// See <https://www.man7.org/linux/man-pages/man3/crypt.3.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn crypt_r(
key: *const c_char,
setting: *const c_char,
data: *mut crypt_data,
) -> *mut c_char {
if unsafe { (*data).initialized } == 0 {
unsafe { *data = crypt_data::new() };
}
let key = unsafe { CStr::from_ptr(key) }
.to_str()
.expect("key must be utf-8");
let setting = unsafe { CStr::from_ptr(setting) }
.to_str()
.expect("setting must be utf-8");
let encoded = if setting.starts_with('$') {
if setting.starts_with("$1$") {
crypt_md5(key, setting)
} else if setting.starts_with("$2") && setting.as_bytes().get(3) == Some(&b'$') {
crypt_blowfish(key, setting)
} else if setting.starts_with("$5$") {
crypt_sha(key, setting, Sha256)
} else if setting.starts_with("$6$") {
crypt_sha(key, setting, Sha512)
} else if setting.starts_with("$7$") {
crypt_scrypt(key, setting)
} else if setting.starts_with("$8$") {
crypt_pbkdf2(key, setting)
} else if setting.starts_with("$argon2") {
crypt_argon2(key, setting)
} else {
platform::ERRNO.set(EINVAL);
return ptr::null_mut();
}
} else {
None
};
if let Some(inner) = encoded {
let len = inner.len();
if let Ok(ret) = CString::new(inner) {
let ret_ptr = ret.into_raw();
unsafe {
let dst = (*data).buff.as_mut_ptr();
ptr::copy_nonoverlapping(ret_ptr, dst.cast(), len);
}
ret_ptr.cast()
} else {
ptr::null_mut()
}
} else {
ptr::null_mut()
}
}
+64
View File
@@ -0,0 +1,64 @@
use super::gen_salt;
use alloc::string::{String, ToString};
use base64ct::{Base64Bcrypt, Encoding};
use core::str;
use pbkdf2::pbkdf2_hmac;
use sha2::Sha256;
/// Performs PBKDF2 key derivation on a given password with a specific setting.
///
/// # Parameters
/// * `passw`: The password to be hashed. It must be a string slice (`&str`).
/// * `setting`: The settings for the PBKDF2 key derivation. It must be a string slice (`&str`)
/// and should follow the format `$<iter>$<salt>`. The `<iter>` part should be a hexadecimal
/// number representing the iteration count for the PBKDF2 function. The `<salt>` part should
/// be a base64-encoded string representing the salt to be used for the PBKDF2 function.
///
/// # Returns
/// * `Option<String>`: Returns `Some(String)` if the PBKDF2 operation was successful, where the
/// returned string is the result of the PBKDF2 operation formatted according to the Modular
/// Crypt Format (MCF). If the PBKDF2 operation failed, it returns `None`.
///
/// # Errors
/// * If the `setting` does not contain a '$' character.
/// * If the `setting` contains another '$' character after the first one.
/// * If the `<salt>` part of the `setting` is empty.
/// * If the `<iter>` part of the `setting` cannot be converted into a `u32` integer.
///
/// # Example
/// ```
/// let password = "my_password";
/// let setting = "$8$3e8$salt";
/// let result = crypt_pbkdf2(password, setting);
/// assert!(result.is_some());
/// ```
///
/// # Note
/// The `crypt_pbkdf2` function uses the SHA256 hashing algorithm for the PBKDF2 operation.
/// The output of the PBKDF2 operation is base64-encoded using the BCrypt variant of base64.
pub fn crypt_pbkdf2(passw: &str, setting: &str) -> Option<String> {
if let Some((iter_str, salt)) = &setting[3..].split_once('$') {
if salt.contains('$') {
return None;
}
let actual_salt = if !salt.is_empty() {
salt.to_string()
} else {
gen_salt()?
};
let iter = u32::from_str_radix(iter_str, 16).ok()?;
let mut buffer = [0u8; 32];
pbkdf2_hmac::<Sha256>(passw.as_bytes(), actual_salt.as_bytes(), iter, &mut buffer);
Some(format!(
"$8${}${}${}",
iter_str,
salt,
Base64Bcrypt::encode_string(&buffer)
))
} else {
None
}
}
+113
View File
@@ -0,0 +1,113 @@
use super::gen_salt;
use crate::platform::types::{c_uchar, c_uint};
use alloc::string::{String, ToString};
use base64ct::{Base64Bcrypt, Encoding};
use core::str;
use scrypt::{Params, scrypt};
/// Map for encoding and decoding
#[inline(always)]
fn to_digit(c: char, radix: u32) -> Option<u32> {
match c {
'.' => Some(0),
'/' => Some(1),
_ => c.to_digit(radix).map(|d| d + 2),
}
}
/// Decodes a 5 character lengt str value to c_uint
///
/// # Arguments
///
/// * `value` - A string slice that represents a u32 value in base64
///
/// # Returns
///
/// * `Option<c_uint>` - Returns the decoded c_uint value if successful, otherwise None
fn dencode_uint(value: &str) -> Option<c_uint> {
if value.len() != 5 {
return None;
}
value
.chars()
.enumerate()
.try_fold(0 as c_uint, |acc, (i, c)| {
acc.checked_add((to_digit(c, 30)? as c_uint) << (i * 6))
})
}
/// Reads settings for password encryption
///
/// # Arguments
///
/// * `setting` - A string slice that represents the settings
///
/// # Returns
///
/// * `Option<(c_uchar, c_uint, c_uint, String)>` - Returns a tuple containing the settings if successful, otherwise None
fn read_setting(setting: &str) -> Option<(c_uchar, c_uint, c_uint, String)> {
let nlog2 = to_digit(setting.chars().next()?, 30)? as c_uchar;
let r = dencode_uint(&setting[1..6])?;
let p = dencode_uint(&setting[6..11])?;
let salt = &setting[11..];
let actual_salt = if !salt.is_empty() {
salt.to_string()
} else {
gen_salt()?
};
Some((nlog2, r, p, actual_salt))
}
/// Performs Scrypt key derivation on a given password with a specific setting.
///
/// # Parameters
/// * `passw`: The password to be hashed. It must be a string slice (`&str`).
/// * `setting`: The settings for the Scrypt key derivation. It must be a string slice (`&str`)
/// and should follow the format `$<Nlog2>$<r>$<p>$<salt>`. The `<Nlog2>` part should be a decimal
/// number representing the logarithm base 2 of the CPU/memory cost factor N for Scrypt. The `<r>`
/// part should be a decimal number representing the block size r. The `<p>` part should be a decimal
/// number representing the parallelization factor p. The `<salt>` part should be a base64-encoded
/// string representing the salt to be used for the Scrypt function.
///
/// # Returns
/// * `Option<String>`: Returns `Some(String)` if the Scrypt operation was successful, where the
/// returned string is the result of the Scrypt operation formatted according to the Modular
/// Crypt Format (MCF). If the Scrypt operation failed, it returns `None`.
///
/// # Errors
/// * If the `setting` length is less than 14 characters.
/// * If the `scrypt` function fails to perform the Scrypt operation.
///
/// # Example
/// ```
/// let password = "my_password";
/// let setting = "$7$C6..../....SodiumChloride";
/// let result = crypt_scrypt(password, setting);
/// assert!(result.is_some());
/// ```
///
/// # Note
/// The `crypt_scrypt` function uses the Scrypt key derivation function for hashing.
/// The output of the Scrypt operation is base64-encoded using the BCrypt variant of base64.
pub fn crypt_scrypt(passw: &str, setting: &str) -> Option<String> {
if setting.len() < 14 {
return None;
}
let (nlog2, r, p, salt) = read_setting(&setting[3..])?;
let params = Params::new(nlog2, r, p, 32).ok()?;
let mut output = [0u8; 32];
scrypt(passw.as_bytes(), salt.as_bytes(), &params, &mut output).ok()?;
Some(format!(
"$7${}${}${}",
&setting[3..14],
salt,
Base64Bcrypt::encode_string(&output)
))
}
+141
View File
@@ -0,0 +1,141 @@
use alloc::string::{String, ToString};
use sha_crypt::{
ROUNDS_DEFAULT, ROUNDS_MAX, ROUNDS_MIN, Sha256Params, Sha512Params, sha256_crypt_b64,
sha512_crypt_b64,
};
use crate::platform::types::c_ulong;
// key limit is not part of the original design, added for DoS protection.
// rounds limit has been lowered (versus the reference/spec), also for DoS
// protection. runtime is O(klen^2 + klen*rounds)
const KEY_MAX: usize = 256;
const SALT_MAX: usize = 16;
const RSTRING: &str = "rounds=";
pub enum ShaType {
Sha256,
Sha512,
}
/// Performs SHA hashing on a given password with a specific setting.
///
/// # Parameters
/// * `passw`: The password to be hashed. It must be a string slice (`&str`).
/// * `setting`: The settings for the SHA hashing. It must be a string slice (`&str`)
/// and should start with "$5$" for SHA256 or "$6$" for SHA512. The rest of the string should represent the salt
/// to be used for the SHA hashing.
/// * `cipher`: The type of SHA algorithm to use. It should be either `ShaType::Sha256` or `ShaType::Sha512`.
///
/// # Returns
/// * `Option<String>`: Returns `Some(String)` if the SHA operation was successful, where the
/// returned string is the result of the SHA operation formatted according to the Modular
/// Crypt Format (MCF). If the SHA operation failed, it returns `None`.
///
/// # Errors
/// * If the `passw` length exceeds `KEY_MAX`.
/// * If the `setting` does not start with "$5$" or "$6$".
/// * If the `setting` does not contain a '$' character.
/// * If the `setting` contains another '$' character after the first one.
/// * If the `setting` contains invalid characters.
/// * If the `setting` contains an invalid number of rounds.
/// * If the `sha256_crypt_b64` or `sha512_crypt_b64` function fails to hash the password.
///
/// # Example
/// ```
/// let password = "my_password";
/// let setting = "$5$rounds=1400$anotherlongsaltstringg";
/// let result = crypt_sha(password, setting, ShaType::Sha256);
/// assert!(result.is_some());
/// ```
///
/// # Note
/// The `crypt_sha` function uses the SHA256 or SHA512 hashing algorithm for hashing.
/// The output of the SHA operation is base64-encoded using the BCrypt variant of base64.
pub fn crypt_sha(passw: &str, setting: &str, cipher: ShaType) -> Option<String> {
let mut cursor = 3;
let rounds;
/* reject large keys */
if passw.len() > KEY_MAX {
return None;
}
// SHA256
// setting: $5$rounds=n$setting$ (rounds=n$ and closing $ are optional)
// SHA512
// setting: $6$rounds=n$setting$ (rounds=n$ and closing $ are optional)
let param = match cipher {
ShaType::Sha256 => "$5$",
ShaType::Sha512 => "$6$",
};
if &setting[0..3] != param {
return None;
}
let has_round;
// 7 is len("rounds=")
if &setting[cursor..cursor + 7] == RSTRING {
cursor += 7;
has_round = true;
if let Some(c_end) = setting[cursor..].chars().position(|r| r == '$') {
if let Ok(u) = setting[cursor..cursor + c_end].parse::<c_ulong>() {
cursor += c_end + 1;
rounds = u.min(ROUNDS_MAX as c_ulong).max(ROUNDS_MIN as c_ulong);
} else {
return None;
}
} else {
return None;
}
} else {
has_round = false;
rounds = ROUNDS_DEFAULT as c_ulong;
}
let mut slen = cursor;
for i in 0..SALT_MAX.min(setting.len() - cursor) {
let idx = cursor + i;
if &setting[idx..idx + 1] == "$" {
break;
}
// reject characters that interfere with /etc/shadow parsing
if &setting[idx..idx + 1] == "\n" || &setting[idx..idx + 1] == ":" {
return None;
}
slen += 1;
}
let setting = &setting[cursor..slen];
if let Ok(enc) = match cipher {
ShaType::Sha256 => {
let params = Sha256Params::new(rounds as usize)
.unwrap_or(Sha256Params::new(ROUNDS_DEFAULT).unwrap());
sha256_crypt_b64(passw.as_bytes(), setting.as_bytes(), &params)
}
ShaType::Sha512 => {
let params = Sha512Params::new(rounds as usize)
.unwrap_or(Sha512Params::new(ROUNDS_DEFAULT).unwrap());
sha512_crypt_b64(passw.as_bytes(), setting.as_bytes(), &params)
}
} {
let (r_slice, rn_slice) = if has_round {
(RSTRING, rounds.to_string() + "$")
} else {
("", String::new())
};
Some(format!(
"{}{}{}{}${}",
param, r_slice, rn_slice, setting, enc
))
} else {
None
}
}
+14 -2
View File
@@ -1,8 +1,20 @@
sys_includes = ["bits/ctype.h"]
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/ctype.h.html
#
# Spec quotations relating to includes:
# - "The <ctype.h> header shall define the locale_t type as described in <locale.h>, representing a locale object."
#
# features.h included for #[deprecated] annotation
sys_includes = ["features.h", "stddef.h"]
include_guard = "_RELIBC_CTYPE_H"
after_includes = """
#include <bits/locale-t.h> // for locale_t
#define _tolower(c) tolower(c)
#define _toupper(c) toupper(c)
"""
language = "C"
style = "Tag"
no_includes = true
no_includes = false
cpp_compat = true
[enum]
+137 -37
View File
@@ -1,53 +1,117 @@
//! ctype implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/ctype.h.html
//! `ctype.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/ctype.h.html>.
use crate::platform::types::*;
use crate::{header::bits_locale_t::locale_t, platform::types::c_int};
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isalnum.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isalnum(c: c_int) -> c_int {
c_int::from(isdigit(c) != 0 || isalpha(c) != 0)
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isalnum_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isalnum_l(c: c_int, _loc: locale_t) -> c_int {
isalnum(c)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isalpha.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isalpha(c: c_int) -> c_int {
c_int::from(islower(c) != 0 || isupper(c) != 0)
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isalpha_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isalpha_l(c: c_int, _loc: locale_t) -> c_int {
isalpha(c)
}
/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/isascii.html>.
///
/// The `isascii()` function was marked obsolescent in the Open Group Base
/// Specifications Issue 7, and removed in Issue 8.
#[deprecated]
#[unsafe(no_mangle)]
pub extern "C" fn isascii(c: c_int) -> c_int {
c_int::from((c & !0x7f) == 0)
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isblank.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isblank(c: c_int) -> c_int {
c_int::from(c == c_int::from(b' ') || c == c_int::from(b'\t'))
}
#[no_mangle]
pub extern "C" fn iscntrl(c: c_int) -> c_int {
c_int::from((c >= 0x00 && c <= 0x1f) || c == 0x7f)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isblank_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isblank_l(c: c_int, _loc: locale_t) -> c_int {
isblank(c)
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/iscntrl.html>.
#[unsafe(no_mangle)]
pub extern "C" fn iscntrl(c: c_int) -> c_int {
c_int::from((0x00..=0x1f).contains(&c) || c == 0x7f)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/iscntrl_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn iscntrl_l(c: c_int, _loc: locale_t) -> c_int {
iscntrl(c)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isdigit.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isdigit(c: c_int) -> c_int {
c_int::from(c >= c_int::from(b'0') && c <= c_int::from(b'9'))
}
#[no_mangle]
pub extern "C" fn isgraph(c: c_int) -> c_int {
c_int::from(c >= 0x21 && c <= 0x7e)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isdigit_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isdigit_l(c: c_int, _loc: locale_t) -> c_int {
isdigit(c)
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isgraph.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isgraph(c: c_int) -> c_int {
c_int::from((0x21..=0x7e).contains(&c))
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isgraph_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isgraph_l(c: c_int, _loc: locale_t) -> c_int {
isgraph(c)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/islower.html>.
#[unsafe(no_mangle)]
pub extern "C" fn islower(c: c_int) -> c_int {
c_int::from(c >= c_int::from(b'a') && c <= c_int::from(b'z'))
}
#[no_mangle]
pub extern "C" fn isprint(c: c_int) -> c_int {
c_int::from(c >= 0x20 && c < 0x7f)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/islower_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn islower_l(c: c_int, _loc: locale_t) -> c_int {
islower(c)
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isprint.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isprint(c: c_int) -> c_int {
c_int::from((0x20..0x7f).contains(&c))
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isprint_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isprint_l(c: c_int, _loc: locale_t) -> c_int {
isprint(c)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ispunct.html>.
#[unsafe(no_mangle)]
pub extern "C" fn ispunct(c: c_int) -> c_int {
c_int::from(
(c >= c_int::from(b'!') && c <= c_int::from(b'/'))
@@ -57,7 +121,14 @@ pub extern "C" fn ispunct(c: c_int) -> c_int {
)
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ispunct_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn ispunct_l(c: c_int, _loc: locale_t) -> c_int {
ispunct(c)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isspace.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isspace(c: c_int) -> c_int {
c_int::from(
c == c_int::from(b' ')
@@ -69,37 +140,66 @@ pub extern "C" fn isspace(c: c_int) -> c_int {
)
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isspace_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isspace_l(c: c_int, _loc: locale_t) -> c_int {
isspace(c)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isupper.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isupper(c: c_int) -> c_int {
c_int::from(c >= c_int::from(b'A') && c <= c_int::from(b'Z'))
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isupper_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isupper_l(c: c_int, _loc: locale_t) -> c_int {
isupper(c)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isxdigit.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isxdigit(c: c_int) -> c_int {
c_int::from(isdigit(c) != 0 || (c | 32 >= c_int::from(b'a') && c | 32 <= c_int::from(b'f')))
}
#[no_mangle]
/// The comment in musl:
/// "nonsense function that should NEVER be used!"
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isxdigit_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isxdigit_l(c: c_int, _loc: locale_t) -> c_int {
isxdigit(c)
}
/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/toascii.html>.
///
/// The `toascii()` function was marked obsolescent in the Open Group Base
/// Specifications Issue 7, and removed in Issue 8.
#[deprecated]
#[unsafe(no_mangle)]
pub extern "C" fn toascii(c: c_int) -> c_int {
c & 0x7f
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tolower.html>.
#[unsafe(no_mangle)]
pub extern "C" fn tolower(c: c_int) -> c_int {
if isupper(c) != 0 {
c | 0x20
} else {
c
}
if isupper(c) != 0 { c | 0x20 } else { c }
}
#[no_mangle]
pub extern "C" fn toupper(c: c_int) -> c_int {
if islower(c) != 0 {
c & !0x20
} else {
c
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tolower_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn tolower_l(c: c_int, _loc: locale_t) -> c_int {
tolower(c)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/toupper.html>.
#[unsafe(no_mangle)]
pub extern "C" fn toupper(c: c_int) -> c_int {
if islower(c) != 0 { c & !0x20 } else { c }
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/toupper_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn toupper_l(c: c_int, _loc: locale_t) -> c_int {
toupper(c)
}
+12 -2
View File
@@ -1,9 +1,19 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dirent.h.html
#
# Spec quotations relating to includes:
# - "The <dirent.h> header shall define the ino_t, reclen_t, size_t, and ssize_t types as described in <sys/types.h>."
sys_includes = ["sys/types.h"]
include_guard = "_RELIBC_DIRENT_H"
language = "C"
style = "Both"
trailer = "#include <bits/dirent.h>"
no_includes = true
after_includes = """
// Shamelessly stolen from musl
// Macros to convert between struct dirent and struct stat types.
#define IFTODT(x) ((x)>>12 & 017)
#define DTTOIF(x) ((x)<<12)
"""
no_includes = false
cpp_compat = true
[enum]
+314 -113
View File
@@ -1,31 +1,171 @@
//! dirent implementation following http://pubs.opengroup.org/onlinepubs/009695399/basedefs/dirent.h.html
//! `dirent.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dirent.h.html>.
use alloc::boxed::Box;
use core::{mem, ptr};
use alloc::{boxed::Box, vec::Vec};
use core::{mem, ptr, slice};
use crate::{
c_str::CStr,
c_vec::CVec,
error::{Errno, ResultExt, ResultExtPtrMut},
fs::File,
header::{errno, fcntl, stdlib, string},
io::{Seek, SeekFrom},
platform::{self, types::*, Pal, Sys},
header::{
errno::{EINVAL, EIO, ENOMEM, ENOTDIR},
fcntl, stdlib, string, sys_stat,
},
out::Out,
platform::{
self, Pal, Sys,
types::{
c_char, c_int, c_long, c_uchar, c_ushort, c_void, ino_t, off_t, reclen_t, size_t,
ssize_t,
},
},
};
const DIR_BUF_SIZE: usize = mem::size_of::<dirent>() * 3;
// values of below constants taken from musl
// No repr(C) needed, C won't see the content
/// Unknown file type.
pub const DT_UNKNOWN: c_int = 0;
/// FIFO special.
pub const DT_FIFO: c_int = 1;
/// Character special.
pub const DT_CHR: c_int = 2;
/// Directory.
pub const DT_DIR: c_int = 4;
/// Block special.
pub const DT_BLK: c_int = 6;
/// Regular.
pub const DT_REG: c_int = 8;
/// Symbolic link.
pub const DT_LNK: c_int = 10;
/// Socket.
pub const DT_SOCK: c_int = 12;
/// Non-POSIX.
/// Whiteout inode.
pub const DT_WHT: c_int = 14;
// values of above constants taken from musl
/// cbindgen:ignore
const INITIAL_BUFSIZE: usize = 512;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dirent.h.html>.
// No repr(C) needed, as this is a completely opaque struct. Being accessed as a pointer, in C it's
// just defined as `struct DIR`.
pub struct DIR {
file: File,
buf: [c_char; DIR_BUF_SIZE],
// index and len are specified in bytes
index: usize,
len: usize,
buf: Vec<u8>,
buf_offset: usize,
// The last value of d_off, used by telldir
offset: usize,
opaque_offset: u64,
}
impl DIR {
pub fn new(path: CStr) -> Result<Box<Self>, Errno> {
Ok(Box::new(Self {
file: File::open(
path,
fcntl::O_RDONLY | fcntl::O_DIRECTORY | fcntl::O_CLOEXEC,
)?,
buf: Vec::with_capacity(INITIAL_BUFSIZE),
buf_offset: 0,
opaque_offset: 0,
}))
}
pub fn from_fd(fd: c_int) -> Result<Box<Self>, Errno> {
let mut stat = sys_stat::stat::default();
Sys::fstat(fd, Out::from_mut(&mut stat))?;
if (stat.st_mode & sys_stat::S_IFMT) != sys_stat::S_IFDIR {
return Err(Errno(ENOTDIR));
}
Sys::fcntl(fd, fcntl::F_SETFD, fcntl::FD_CLOEXEC as _)?;
// Take ownership now but not earlier so we don't close the fd on error.
let file = File::new(fd);
Ok(Self {
file,
buf: Vec::with_capacity(INITIAL_BUFSIZE),
buf_offset: 0,
opaque_offset: 0,
}
.into())
}
fn next_dirent(&mut self) -> Result<*mut dirent, Errno> {
let mut this_dent = self.buf.get(self.buf_offset..).ok_or(Errno(EIO))?;
if this_dent.is_empty() {
let size = loop {
self.buf.resize(self.buf.capacity(), 0_u8);
// TODO: uninitialized memory?
match Sys::getdents(*self.file, &mut self.buf, self.opaque_offset) {
Ok(size) => break size,
Err(Errno(EINVAL)) => {
self.buf
.try_reserve_exact(self.buf.len())
.map_err(|_| Errno(ENOMEM))?;
continue;
}
Err(Errno(other)) => return Err(Errno(other)),
}
};
self.buf.truncate(size);
self.buf_offset = 0;
if size == 0 {
return Ok(core::ptr::null_mut());
}
this_dent = &self.buf;
}
let (this_reclen, this_next_opaque) =
unsafe { Sys::dent_reclen_offset(this_dent, self.buf_offset).ok_or(Errno(EIO))? };
//println!("CDENT {} {}+{}", self.opaque_offset, self.buf_offset, this_reclen);
let next_off = self
.buf_offset
.checked_add(usize::from(this_reclen))
.ok_or(Errno(EIO))?;
if next_off > self.buf.len() {
return Err(Errno(EIO));
}
if this_dent.len() < usize::from(this_reclen) {
// Don't want memory corruption if a scheme is adversarial.
return Err(Errno(EIO));
}
let dent_ptr = this_dent.as_ptr() as *mut dirent;
self.opaque_offset = this_next_opaque;
self.buf_offset = next_off;
Ok(dent_ptr)
}
fn seek(&mut self, off: u64) {
let Ok(_) = Sys::dir_seek(*self.file, off) else {
return;
};
self.buf.clear();
self.buf_offset = 0;
self.opaque_offset = off;
}
fn rewind(&mut self) {
self.opaque_offset = 0;
let Ok(_) = Sys::dir_seek(*self.file, 0) else {
return;
};
self.buf.clear();
self.buf_offset = 0;
self.opaque_offset = 0;
}
fn close(mut self) -> Result<(), Errno> {
// Reference files aren't closed when dropped
self.file.reference = true;
// TODO: result
Sys::close(*self.file)
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dirent.h.html>.
#[repr(C)]
#[derive(Clone)]
pub struct dirent {
@@ -36,69 +176,114 @@ pub struct dirent {
pub d_name: [c_char; 256],
}
#[no_mangle]
pub unsafe extern "C" fn opendir(path: *const c_char) -> *mut DIR {
let path = CStr::from_ptr(path);
let file = match File::open(
path,
fcntl::O_RDONLY | fcntl::O_DIRECTORY | fcntl::O_CLOEXEC,
) {
Ok(file) => file,
Err(_) => return ptr::null_mut(),
};
Box::into_raw(Box::new(DIR {
file,
buf: [0; DIR_BUF_SIZE],
index: 0,
len: 0,
offset: 0,
}))
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dirent.h.html>.
/// must have the same struct layout as dirent
#[repr(C)]
#[derive(Clone)]
pub struct posix_dent {
pub d_ino: ino_t,
pub d_off: off_t, // not specified by posix
pub d_reclen: reclen_t,
pub d_type: c_uchar,
pub d_name: [c_char; 256],
}
#[no_mangle]
pub unsafe extern "C" fn closedir(dir: *mut DIR) -> c_int {
let mut dir = Box::from_raw(dir);
/// cbindgen:ignore
#[cfg(target_os = "redox")]
const _: () = {
use core::mem::{offset_of, size_of};
use syscall::dirent::DirentHeader;
let ret = Sys::close(*dir.file);
// Reference files aren't closed when dropped
dir.file.reference = true;
ret
}
#[no_mangle]
pub unsafe extern "C" fn dirfd(dir: *mut DIR) -> c_int {
*((*dir).file)
}
#[no_mangle]
pub unsafe extern "C" fn readdir(dir: *mut DIR) -> *mut dirent {
if (*dir).index >= (*dir).len {
let read = Sys::getdents(
*(*dir).file,
(*dir).buf.as_mut_ptr() as *mut dirent,
(*dir).buf.len(),
);
if read <= 0 {
if read != 0 && read != -errno::ENOENT {
platform::errno = -read;
}
return ptr::null_mut();
}
(*dir).index = 0;
(*dir).len = read as usize;
if offset_of!(dirent, d_ino) != offset_of!(DirentHeader, inode) {
panic!("struct dirent layout mismatch (inode)");
}
if offset_of!(dirent, d_off) != offset_of!(DirentHeader, next_opaque_id) {
panic!("struct dirent layout mismatch (inode)");
}
if offset_of!(dirent, d_reclen) != offset_of!(DirentHeader, record_len) {
panic!("struct dirent layout mismatch (len)");
}
if offset_of!(dirent, d_type) != offset_of!(DirentHeader, kind) {
panic!("struct dirent layout mismatch (kind)");
}
if offset_of!(dirent, d_name) != size_of::<DirentHeader>() {
panic!("struct dirent layout mismatch (name)");
}
};
let ptr = (*dir).buf.as_mut_ptr().add((*dir).index) as *mut dirent;
(*dir).offset = (*ptr).d_off as usize;
(*dir).index += (*ptr).d_reclen as usize;
ptr
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/alphasort.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn alphasort(first: *mut *const dirent, second: *mut *const dirent) -> c_int {
unsafe { string::strcoll((**first).d_name.as_ptr(), (**second).d_name.as_ptr()) }
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/closedir.html>.
#[unsafe(no_mangle)]
pub extern "C" fn closedir(dir: Box<DIR>) -> c_int {
dir.close().map(|()| 0).or_minus_one_errno()
}
/// See <https://man.freebsd.org/cgi/man.cgi?query=fdopendir&sektion=3>
///
/// FreeBSD extension that transfers ownership of the directory file descriptor to the user.
///
/// It doesn't matter if DIR was opened with [`opendir`] or [`fdopendir`].
#[unsafe(no_mangle)]
pub extern "C" fn fdclosedir(dir: Box<DIR>) -> c_int {
let mut file = dir.file;
file.reference = true;
*file
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dirfd.html>.
#[unsafe(no_mangle)]
pub extern "C" fn dirfd(dir: &mut DIR) -> c_int {
*dir.file
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fdopendir.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn opendir(path: *const c_char) -> *mut DIR {
let path = unsafe { CStr::from_ptr(path) };
DIR::new(path).or_errno_null_mut()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fdopendir.html>.
#[unsafe(no_mangle)]
pub extern "C" fn fdopendir(fd: c_int) -> *mut DIR {
DIR::from_fd(fd).or_errno_null_mut()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_getdents.html>.
#[unsafe(no_mangle)]
pub extern "C" fn posix_getdents(
fildes: c_int,
buf: *mut c_void,
nbyte: size_t,
_flags: c_int,
) -> ssize_t {
let slice = unsafe { slice::from_raw_parts_mut(buf.cast::<u8>(), nbyte) };
Sys::posix_getdents(fildes, slice)
.map(|s| s as ssize_t)
.or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/readdir.html>.
#[unsafe(no_mangle)]
pub extern "C" fn readdir(dir: &mut DIR) -> *mut dirent {
dir.next_dirent().or_errno_null_mut()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/readdir.html>.
///
/// # Deprecation
/// The `readdir_r()` function was marked obsolescent in the Open Group Base
/// Specifications Issue 8.
#[deprecated]
// #[unsafe(no_mangle)]
pub extern "C" fn readdir_r(
_dir: *mut DIR,
_entry: *mut dirent,
@@ -107,35 +292,21 @@ pub extern "C" fn readdir_r(
unimplemented!(); // plus, deprecated
}
#[no_mangle]
pub unsafe extern "C" fn telldir(dir: *mut DIR) -> c_long {
(*dir).offset as c_long
}
#[no_mangle]
pub unsafe extern "C" fn seekdir(dir: *mut DIR, off: c_long) {
let _ = (*dir).file.seek(SeekFrom::Start(off as u64));
(*dir).offset = off as usize;
(*dir).index = 0;
(*dir).len = 0;
}
#[no_mangle]
pub unsafe extern "C" fn rewinddir(dir: *mut DIR) {
seekdir(dir, 0)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/rewinddir.html>.
#[unsafe(no_mangle)]
pub extern "C" fn rewinddir(dir: &mut DIR) {
dir.rewind();
}
#[no_mangle]
pub unsafe extern "C" fn alphasort(first: *mut *const dirent, second: *mut *const dirent) -> c_int {
string::strcoll((**first).d_name.as_ptr(), (**second).d_name.as_ptr())
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/alphasort.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn scandir(
dirp: *const c_char,
namelist: *mut *mut *mut dirent,
filter: Option<extern "C" fn(_: *const dirent) -> c_int>,
compare: Option<extern "C" fn(_: *mut *const dirent, _: *mut *const dirent) -> c_int>,
) -> c_int {
let dir = opendir(dirp);
let dir = unsafe { opendir(dirp) };
if dir.is_null() {
return -1;
}
@@ -145,54 +316,84 @@ pub unsafe extern "C" fn scandir(
Err(err) => return -1,
};
let old_errno = platform::errno;
platform::errno = 0;
let old_errno = platform::ERRNO.get();
platform::ERRNO.set(0);
loop {
let entry: *mut dirent = readdir(dir);
let entry: *mut dirent = readdir(unsafe { &mut *dir });
if entry.is_null() {
break;
}
if let Some(filter) = filter {
if filter(entry) == 0 {
continue;
}
if let Some(filter) = filter
&& filter(entry) == 0
{
continue;
}
let copy = platform::alloc(mem::size_of::<dirent>()) as *mut dirent;
let copy = unsafe { platform::alloc(mem::size_of::<dirent>()) }.cast::<dirent>();
if copy.is_null() {
break;
}
ptr::write(copy, (*entry).clone());
if let Err(_) = vec.push(copy) {
unsafe { ptr::write(copy, (*entry).clone()) };
if vec.push(copy).is_err() {
break;
}
}
closedir(dir);
closedir(unsafe { Box::from_raw(dir) });
let len = vec.len();
if let Err(_) = vec.shrink_to_fit() {
if vec.shrink_to_fit().is_err() {
return -1;
}
if platform::errno != 0 {
if platform::ERRNO.get() != 0 {
for ptr in &mut vec {
platform::free(*ptr as *mut c_void);
unsafe { platform::free((*ptr).cast::<c_void>()) };
}
-1
} else {
*namelist = vec.leak();
unsafe {
// Empty CVecs use a dangling pointer which cannot be freed, return null instead
if vec.is_empty() {
*namelist = ptr::null_mut();
} else {
*namelist = vec.leak();
}
}
platform::errno = old_errno;
stdlib::qsort(
*namelist as *mut c_void,
len as size_t,
mem::size_of::<*mut dirent>(),
mem::transmute(compare),
);
platform::ERRNO.set(old_errno);
unsafe {
stdlib::qsort(
(*namelist).cast::<c_void>(),
len as size_t,
mem::size_of::<*mut dirent>(),
#[allow(clippy::missing_transmute_annotations)] // too verbose
mem::transmute(compare),
)
};
len as c_int
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/seekdir.html>.
#[unsafe(no_mangle)]
pub extern "C" fn seekdir(dir: &mut DIR, off: c_long) {
let Ok(off) = off.try_into() else {
platform::ERRNO.set(EINVAL);
return;
};
dir.seek(off);
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/telldir.html>.
#[unsafe(no_mangle)]
pub extern "C" fn telldir(dir: &mut DIR) -> c_long {
dir.opaque_offset as c_long
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cbindgen_stupid_struct_user_for_posix_dent(_: posix_dent) {}
+90 -32
View File
@@ -1,44 +1,102 @@
//! dl-tls implementation for Redox
use crate::{ld_so::tcb::Tcb, platform::types::*};
// FIXME(andypython): remove this when #![allow(warnings, unused_variables)] is
// dropped from src/lib.rs.
#![warn(warnings, unused_variables)]
#[cfg(target_arch = "x86")]
use core::arch::global_asm;
use alloc::alloc::alloc;
use core::{alloc::Layout, ptr};
use crate::{ld_so::tcb::Tcb, platform::types::c_void};
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct dl_tls_index {
pub ti_module: u64,
pub ti_offset: u64,
pub ti_module: usize,
pub ti_offset: usize,
}
#[no_mangle]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __tls_get_addr(ti: *mut dl_tls_index) -> *mut c_void {
trace!(
"__tls_get_addr({:p}: {:#x}, {:#x})",
let tcb = unsafe { Tcb::current().unwrap() };
let ti = unsafe { &*ti };
let masters = tcb.masters().unwrap();
#[cfg(feature = "trace_tls")]
log::trace!(
"__tls_get_addr({:p}: {:#x}, {:#x}, masters_len={}, dtv_len={})",
ti,
(*ti).ti_module,
(*ti).ti_offset
ti.ti_module,
ti.ti_offset,
masters.len(),
tcb.dtv_mut().len()
);
if let Some(tcb) = Tcb::current() {
if let Some(tls) = tcb.tls() {
if let Some(masters) = tcb.masters() {
if let Some(master) = masters.get((*ti).ti_module as usize) {
let addr = tls
.as_mut_ptr()
.add(master.offset + (*ti).ti_offset as usize);
trace!(
"__tls_get_addr({:p}: {:#x}, {:#x}) = {:p}",
ti,
(*ti).ti_module,
(*ti).ti_offset,
addr
);
return addr as *mut c_void;
}
}
}
if tcb.dtv_mut().len() < masters.len() {
// Reallocate DTV.
tcb.setup_dtv(masters.len());
}
panic!(
"__tls_get_addr({:p}: {:#x}, {:#x}) failed",
ti,
(*ti).ti_module,
(*ti).ti_offset
);
let dtv_index = ti.ti_module - 1;
if tcb.dtv_mut()[dtv_index].is_null() {
// Allocate TLS for module.
let master = &masters[dtv_index];
let module_tls = unsafe {
// FIXME(andypython): master.align
let layout = Layout::from_size_align_unchecked(master.segment_size, 16);
let ptr = alloc(layout);
ptr::copy_nonoverlapping(master.ptr, ptr, master.image_size);
ptr::write_bytes(
ptr.add(master.image_size),
0,
master.segment_size - master.image_size,
);
ptr
};
// Set the DTV entry.
tcb.dtv_mut()[dtv_index] = module_tls;
}
let mut ptr = tcb.dtv_mut()[dtv_index];
if ptr.is_null() {
panic!(
"__tls_get_addr({ti:p}: {:#x}, {:#x})",
ti.ti_module, ti.ti_offset
);
}
if cfg!(target_arch = "riscv64") {
ptr = unsafe { ptr.add(0x800 + ti.ti_offset) }; // dynamic offsets are 0x800-based on risc-v
} else {
ptr = unsafe { ptr.add(ti.ti_offset) };
}
ptr.cast::<c_void>()
}
// x86 can define a version that passes a pointer to dl_tls_index in eax
#[cfg(target_arch = "x86")]
global_asm!(
"
.globl ___tls_get_addr
.type ___tls_get_addr, @function
___tls_get_addr:
push ebp
mov ebp, esp
push eax
call __tls_get_addr
add esp, 4
pop ebp
ret
.size ___tls_get_addr, . - ___tls_get_addr
"
);
+3
View File
@@ -1,3 +1,6 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dlfcn.h.html
#
# There are no spec quotations relating to includes
sys_includes = []
include_guard = "_RELIBC_DLFCN_H"
language = "C"
+103 -67
View File
@@ -1,161 +1,197 @@
//! dlfcn implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/dlfcn.h.html
//! `dlfcn.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dlfcn.h.html>.
// FIXME(andypython): remove this when #![allow(warnings, unused_variables)] is
// dropped from src/lib.rs.
#![warn(warnings, unused_variables)]
use core::{
ptr, str,
sync::atomic::{AtomicUsize, Ordering},
};
use crate::{c_str::CStr, ld_so::tcb::Tcb, platform::types::*};
use crate::{
c_str::CStr,
ld_so::{
linker::{DlError, ObjectHandle, Resolve, ScopeKind},
tcb::Tcb,
},
platform::types::{c_char, c_int, c_void},
};
pub const RTLD_LAZY: c_int = 0x0001;
pub const RTLD_NOW: c_int = 0x0002;
pub const RTLD_GLOBAL: c_int = 0x0100;
pub const RTLD_LAZY: c_int = 1 << 0;
pub const RTLD_NOW: c_int = 1 << 1;
pub const RTLD_NOLOAD: c_int = 1 << 2;
pub const RTLD_GLOBAL: c_int = 1 << 8;
pub const RTLD_LOCAL: c_int = 0x0000;
static ERROR_NOT_SUPPORTED: &'static CStr = c_str!("dlfcn not supported");
#[allow(clippy::zero_ptr)] // related cbindgen issue: https://github.com/mozilla/cbindgen/issues/948
pub const RTLD_DEFAULT: *mut c_void = 0 as *mut c_void; // XXX: cbindgen doesn't like ptr::null_mut() for publically exported constants
static ERROR_NOT_SUPPORTED: &core::ffi::CStr = c"dlfcn not supported";
#[thread_local]
static ERROR: AtomicUsize = AtomicUsize::new(0);
fn set_last_error(error: DlError) {
ERROR.store(error.repr().as_ptr() as usize, Ordering::SeqCst);
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dlfcn.h.html>.
#[repr(C)]
pub struct Dl_info {
#[allow(non_camel_case_types)]
pub struct Dl_info_t {
dli_fname: *const c_char,
dli_fbase: *mut c_void,
dli_sname: *const c_char,
dli_saddr: *mut c_void,
}
#[no_mangle]
pub unsafe extern "C" fn dladdr(addr: *mut c_void, info: *mut Dl_info) -> c_int {
/// alias as per spec update: <https://www.austingroupbugs.net/view.php?id=1847>
pub type Dl_info = Dl_info_t;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dladdr.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dladdr(_addr: *const c_void, info: *mut Dl_info_t) -> c_int {
//TODO
(*info).dli_fname = ptr::null();
(*info).dli_fbase = ptr::null_mut();
(*info).dli_sname = ptr::null();
(*info).dli_saddr = ptr::null_mut();
unsafe {
(*info).dli_fname = ptr::null();
(*info).dli_fbase = ptr::null_mut();
(*info).dli_sname = ptr::null();
(*info).dli_saddr = ptr::null_mut();
}
0
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dlopen.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dlopen(cfilename: *const c_char, flags: c_int) -> *mut c_void {
//TODO support all sort of flags
let resolve = if flags & RTLD_NOW == RTLD_NOW {
Resolve::Now
} else {
Resolve::Lazy
};
let scope = if flags & RTLD_GLOBAL == RTLD_GLOBAL {
ScopeKind::Global
} else {
ScopeKind::Local
};
let noload = flags & RTLD_NOLOAD == RTLD_NOLOAD;
let filename = if cfilename.is_null() {
None
} else {
Some(str::from_utf8_unchecked(
CStr::from_ptr(cfilename).to_bytes(),
))
unsafe {
Some(str::from_utf8_unchecked(
CStr::from_ptr(cfilename).to_bytes(),
))
}
};
let tcb = match Tcb::current() {
let tcb = match unsafe { Tcb::current() } {
Some(tcb) => tcb,
None => {
eprintln!("dlopen: tcb not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
};
if tcb.linker_ptr.is_null() {
eprintln!("dlopen: linker not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
let mut linker = (&*tcb.linker_ptr).lock();
let mut linker = unsafe { (*tcb.linker_ptr).lock() };
let cbs_c = linker.cbs.clone();
let cbs = cbs_c.borrow();
let id = match (cbs.load_library)(&mut linker, filename) {
Err(err) => {
eprintln!("dlopen: failed to load {:?}", filename);
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
match (cbs.load_library)(&mut linker, filename, resolve, scope, noload) {
Ok(handle) => handle.as_ptr().cast_mut(),
Err(error) => {
set_last_error(error);
ptr::null_mut()
}
Ok(id) => id,
};
if let Some(fname) = filename {
if let Err(err) = (cbs.link)(&mut linker, None, None, Some(id)) {
(cbs.unload)(&mut linker, id);
eprintln!("dlopen: failed to link '{}': {}", fname, err);
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
};
if let Err(err) = (cbs.run_init)(&mut linker, Some(id)) {
(cbs.unload)(&mut linker, id);
eprintln!("dlopen: failed to link '{}': {}", fname, err);
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
};
}
id as *mut c_void
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dlsym.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void {
let handle = ObjectHandle::from_ptr(handle);
if symbol.is_null() {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
let symbol_str = str::from_utf8_unchecked(CStr::from_ptr(symbol).to_bytes());
let symbol_str = unsafe { str::from_utf8_unchecked(CStr::from_ptr(symbol).to_bytes()) };
let tcb = match Tcb::current() {
// FIXME(andypython): just call obj.scope.get_sym() directly or search the
// global scope. The rest is unnecessary as Linker::get_sym() does not
// depend on the Linker state.
let tcb = match unsafe { Tcb::current() } {
Some(tcb) => tcb,
None => {
eprintln!("dlsym: tcb not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
};
if tcb.linker_ptr.is_null() {
eprintln!("dlsym: linker not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
let linker = (&*tcb.linker_ptr).lock();
let linker = unsafe { (*tcb.linker_ptr).lock() };
let cbs_c = linker.cbs.clone();
let cbs = cbs_c.borrow();
if let Some(global) = (cbs.get_sym)(&linker, symbol_str, Some(handle as usize)) {
global.as_ptr()
} else {
eprintln!("dlsym: symbol not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
ptr::null_mut()
match (cbs.get_sym)(&linker, handle, symbol_str) {
Some(sym) => sym,
_ => {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
ptr::null_mut()
}
}
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dlclose.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dlclose(handle: *mut c_void) -> c_int {
let tcb = match Tcb::current() {
let tcb = match unsafe { Tcb::current() } {
Some(tcb) => tcb,
None => {
eprintln!("dlclose: tcb not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return -1;
}
};
if tcb.linker_ptr.is_null() {
eprintln!("dlclose: linker not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return -1;
};
let mut linker = (&*tcb.linker_ptr).lock();
let Some(handle) = ObjectHandle::from_ptr(handle) else {
set_last_error(DlError::InvalidHandle);
return -1;
};
let mut linker = unsafe { (*tcb.linker_ptr).lock() };
let cbs_c = linker.cbs.clone();
let cbs = cbs_c.borrow();
if let Err(err) = (cbs.run_fini)(&mut linker, Some(handle as usize)) {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return -1;
};
(cbs.unload)(&mut linker, handle as usize);
(cbs.unload)(&mut linker, handle);
0
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dlerror.html>.
#[unsafe(no_mangle)]
pub extern "C" fn dlerror() -> *mut c_char {
ERROR.swap(0, Ordering::SeqCst) as *mut c_char
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cbindgen_stupid_alias_dlinfo_for_dladdr(_: Dl_info) {}
+38 -1
View File
@@ -1,4 +1,41 @@
sys_includes = ["bits/elf.h"]
after_includes = """
#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4)
#define ELF32_ST_TYPE(val) ((val) & 0xf)
#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf))
#define ELF64_ST_BIND(val) ELF32_ST_BIND (val)
#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val)
#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type))
#define ELF32_ST_VISIBILITY(o) ((o) & 0x03)
#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o)
#define ELF32_R_SYM(val) ((val) >> 8)
#define ELF32_R_TYPE(val) ((val) & 0xff)
#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff))
#define ELF64_R_SYM(i) ((i) >> 32)
#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type))
#define DT_VALRNGHI 0x6ffffdff
#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag))
#define DT_ADDRRNGHI 0x6ffffeff
#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag))
#define DT_VERNEEDNUM 0x6fffffff
#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag))
#define ELF32_M_SYM(info) ((info) >> 8)
#define ELF32_M_SIZE(info) ((unsigned char) (info))
#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size))
#define ELF64_M_SYM(info) ELF32_M_SYM (info)
#define ELF64_M_SIZE(info) ELF32_M_SIZE (info)
#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size)
"""
include_guard = "_ELF_H"
language = "C"
style = "Tag"
+28 -8
View File
@@ -1,12 +1,16 @@
use crate::platform::types::*;
//! `elf.h` implementation.
//!
//! Non-POSIX, see <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
use crate::platform::types::{c_char, c_uchar, int32_t, int64_t, uint16_t, uint32_t, uint64_t};
pub type Elf32_Half = uint16_t;
pub type Elf64_Half = uint16_t;
pub type Elf32_Word = uint32_t;
pub type Elf32_Sword = int32_t;
pub type Elf64_Word = uint64_t;
pub type Elf64_Sword = int64_t;
pub type Elf64_Word = uint32_t;
pub type Elf64_Sword = int32_t;
pub type Elf32_Xword = uint64_t;
pub type Elf32_Sxword = int64_t;
@@ -27,6 +31,7 @@ pub type Elf64_Versym = Elf64_Half;
pub const EI_NIDENT: usize = 16;
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Ehdr {
pub e_ident: [c_uchar; EI_NIDENT],
@@ -45,6 +50,7 @@ pub struct Elf32_Ehdr {
pub e_shstrndx: Elf32_Half,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Ehdr {
pub e_ident: [c_uchar; EI_NIDENT],
@@ -71,7 +77,7 @@ pub const EI_MAG2: usize = 2;
pub const ELFMAG2: c_char = 'L' as c_char;
pub const EI_MAG3: usize = 3;
pub const ELFMAG3: c_char = 'F' as c_char;
pub const ELFMAG: &'static str = "\x7fELF";
pub const ELFMAG: &str = "\x7fELF";
pub const SELFMAG: usize = 4;
pub const EI_CLASS: usize = 4;
pub const ELFCLASSNONE: usize = 0;
@@ -196,6 +202,7 @@ pub const EV_NONE: usize = 0;
pub const EV_CURRENT: usize = 1;
pub const EV_NUM: usize = 2;
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Shdr {
pub sh_name: Elf32_Word,
@@ -210,6 +217,7 @@ pub struct Elf32_Shdr {
pub sh_entsize: Elf32_Word,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Shdr {
pub sh_name: Elf64_Word,
@@ -289,6 +297,7 @@ pub const SHF_ORDERED: usize = 1 << 30;
pub const SHF_EXCLUDE: usize = 1 << 31;
pub const GRP_COMDAT: usize = 0x1;
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Sym {
pub st_name: Elf32_Word,
@@ -299,6 +308,7 @@ pub struct Elf32_Sym {
pub st_shndx: Elf32_Section,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Sym {
pub st_name: Elf64_Word,
@@ -361,18 +371,21 @@ pub const STV_INTERNAL: usize = 1;
pub const STV_HIDDEN: usize = 2;
pub const STV_PROTECTED: usize = 3;
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Rel {
pub r_offset: Elf32_Addr,
pub r_info: Elf32_Word,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Rel {
pub r_offset: Elf64_Addr,
pub r_info: Elf64_Xword,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Rela {
pub r_offset: Elf32_Addr,
@@ -380,6 +393,7 @@ pub struct Elf32_Rela {
pub r_addend: Elf32_Sword,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Rela {
pub r_offset: Elf64_Addr,
@@ -387,6 +401,7 @@ pub struct Elf64_Rela {
pub r_addend: Elf64_Sxword,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Phdr {
pub p_type: Elf32_Word,
@@ -399,6 +414,7 @@ pub struct Elf32_Phdr {
pub p_align: Elf32_Word,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Phdr {
pub p_type: Elf64_Word,
@@ -487,6 +503,7 @@ pub union Elf32_Dyn_Union {
d_ptr: Elf32_Addr,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Dyn {
pub d_tag: Elf32_Sword,
@@ -499,6 +516,7 @@ pub union Elf64_Dyn_Union {
d_ptr: Elf64_Addr,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Dyn {
pub d_tag: Elf64_Sxword,
@@ -740,6 +758,7 @@ pub const AT_L1D_CACHESHAPE: usize = 35;
pub const AT_L2_CACHESHAPE: usize = 36;
pub const AT_L3_CACHESHAPE: usize = 37;
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Nhdr {
pub n_namesz: Elf32_Word,
@@ -747,6 +766,7 @@ pub struct Elf32_Nhdr {
pub n_type: Elf32_Word,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Nhdr {
pub n_namesz: Elf64_Word,
@@ -754,8 +774,8 @@ pub struct Elf64_Nhdr {
pub n_type: Elf64_Word,
}
pub const ELF_NOTE_SOLARIS: &'static str = "SUNW Solaris";
pub const ELF_NOTE_GNU: &'static str = "GNU";
pub const ELF_NOTE_SOLARIS: &str = "SUNW Solaris";
pub const ELF_NOTE_GNU: &str = "GNU";
pub const ELF_NOTE_PAGESIZE_HINT: usize = 1;
@@ -943,8 +963,8 @@ pub const R_X86_64_IRELATIVE: usize = 37;
pub const R_X86_64_RELATIVE64: usize = 38;
pub const R_X86_64_NUM: usize = 39;
#[no_mangle]
pub extern "C" fn stupid_cbindgen_needs_a_function_that_holds_all_elf_structs(
#[unsafe(no_mangle)]
pub extern "C" fn _cbindgen_export_elf(
a: Elf32_Ehdr,
b: Elf64_Ehdr,
c: Elf32_Shdr,
+12
View File
@@ -0,0 +1,12 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/endian.h.html
#
# Spec quotations relating to includes:
# - "The <endian.h> header shall define the uint16_t, uint32_t, and uint64_t types as described in <stdint.h>."
# - "Inclusion of the <endian.h> header may also make visible all symbols from <stdint.h>."
sys_includes = ["stdint.h"]
include_guard = "_RELIBC_ENDIAN_H"
trailer = "#include <machine/endian.h>"
language = "C"
style = "Tag"
no_includes = true
cpp_compat = true
+77
View File
@@ -0,0 +1,77 @@
//! `endian.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/endian.h.html>.
use crate::platform::types::{uint16_t, uint32_t, uint64_t};
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn be16toh(x: uint16_t) -> uint16_t {
uint16_t::from_be(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn be32toh(x: uint32_t) -> uint32_t {
uint32_t::from_be(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn be64toh(x: uint64_t) -> uint64_t {
uint64_t::from_be(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn htobe16(x: uint16_t) -> uint16_t {
x.to_be()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn htobe32(x: uint32_t) -> uint32_t {
x.to_be()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn htobe64(x: uint64_t) -> uint64_t {
x.to_be()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn htole16(x: uint16_t) -> uint16_t {
x.to_le()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn htole32(x: uint32_t) -> uint32_t {
x.to_le()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn htole64(x: uint64_t) -> uint64_t {
x.to_le()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn le16toh(x: uint16_t) -> uint16_t {
uint16_t::from_le(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn le32toh(x: uint32_t) -> uint32_t {
uint32_t::from_le(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn le64toh(x: uint64_t) -> uint64_t {
uint64_t::from_le(x)
}
+5
View File
@@ -0,0 +1,5 @@
sys_includes = ["stdarg.h", "stdio.h", "features.h"]
include_guard = "_RELIBC_ERR_H"
language = "C"
no_includes = false
cpp_compat = true
+204
View File
@@ -0,0 +1,204 @@
//! `err.h` implementation.
//!
//! See <https://man.freebsd.org/cgi/man.cgi?err>
//!
//! `err.h` is a BSD extension to the C library which provides functions for printing formatted
//! errors. Errors are printed to [`stdio::stderr`] by default or to a file set by
//! [`err_set_file`]. This family of functions is non-portable, but it is also supported by `glibc`
//! and `musl`.
//!
//! The functions come in sets of three. Each of them print the program binary name (the last path
//! segment of `argv[0]`) and an optional user message along with these differences:
//! * No suffix: Prints an error message for ERRNO based on [`strerror`]
//! * `c` suffix: Prints an error message for an arbitrary error code
//! * `x` suffix: Does not print an error code
//!
//! For example, `err` does not have a suffix so it would print the program name, the user message,
//! and an error string for ERRNO. `errc` would operate in the same way except the functions takes
//! an error code for which to print an error string.
// Allow is intentional. Almost every line of the simple functions below are unsafe.
// unsafe_op_in_unsafe_fn only adds visual noise or a needless indentation here.
#![allow(unsafe_op_in_unsafe_fn)]
use core::{
ffi::{VaList as va_list, c_char, c_int},
ptr,
};
use crate::{
header::{
stdio::{self, FILE, fprintf, fputc, fputs, vfprintf},
stdlib::exit,
string::strerror,
},
platform::{self, ERRNO},
};
// Optional callback from user invoked on exit.
type ExitCallback = Option<unsafe extern "C" fn(c_int)>;
static mut on_exit: ExitCallback = None;
// Messages from this module are written to this sink.
static mut error_sink: *mut FILE = ptr::null_mut();
/// Set global [`FILE`] sink to write errors and warnings.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn err_set_file(fp: *mut FILE) {
if fp.is_null() {
error_sink = stdio::stderr;
} else {
error_sink = fp;
}
}
/// Set or remove a callback to invoke before exiting on error.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn err_set_exit(ef: ExitCallback) {
on_exit = ef;
}
/// Print a user message then an error message for [`ERRNO`] followed by exiting with `eval`.
///
/// The message format is `progname: fmt: strerror(ERRNO)`
///
/// # Return
/// Does not return. Exits with `eval` as an error code.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn err(eval: c_int, fmt: *const c_char, va_list: ...) -> ! {
let code = Some(ERRNO.get());
err_exit(eval, code, fmt, va_list)
}
/// Print a user message then an error message for `code` before exiting with `eval` as a return.
///
/// The message format is `progname: fmt: strerror(code)`
///
/// # Return
/// Exits with `eval` as an error code.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn errc(eval: c_int, code: c_int, fmt: *const c_char, va_list: ...) -> ! {
err_exit(eval, Some(code), fmt, va_list)
}
/// Print a user message then exits with `eval` as a return.
///
/// The message format is `progname: fmt`
///
/// # Return
/// Exits with `eval` as an error code.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn errx(eval: c_int, fmt: *const c_char, va_list: ...) -> ! {
err_exit(eval, None, fmt, va_list)
}
/// Print a user message and then an error message for [`ERRNO`].
///
/// The message format is `progname: fmt: strerror(ERRNO)`
#[unsafe(no_mangle)]
pub unsafe extern "C" fn warn(fmt: *const c_char, va_list: ...) {
let code = Some(ERRNO.get());
display_message(code, fmt, va_list);
}
/// Print a user message then an error message for `code`.
///
/// The message format is `progname: fmt: strerror(code)`
#[unsafe(no_mangle)]
pub unsafe extern "C" fn warnc(code: c_int, fmt: *const c_char, va_list: ...) {
display_message(Some(code), fmt, va_list);
}
/// Print a user message as a warning.
///
/// The message format is `progname: fmt`
#[unsafe(no_mangle)]
pub unsafe extern "C" fn warnx(fmt: *const c_char, va_list: ...) {
display_message(None, fmt, va_list);
}
/// See [`err`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn verr(eval: c_int, fmt: *const c_char, args: va_list) -> ! {
let code = Some(ERRNO.get());
err_exit(eval, code, fmt, args);
}
/// See [`errc`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn verrc(eval: c_int, code: c_int, fmt: *const c_char, args: va_list) -> ! {
err_exit(eval, Some(code), fmt, args)
}
/// See [`errx`];
#[unsafe(no_mangle)]
pub unsafe extern "C" fn verrx(eval: c_int, fmt: *const c_char, args: va_list) -> ! {
err_exit(eval, None, fmt, args)
}
/// See [`warn`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn vwarn(fmt: *const c_char, args: va_list) {
let code = Some(ERRNO.get());
display_message(code, fmt, args);
}
/// See [`warnc`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn vwarnc(code: c_int, fmt: *const c_char, args: va_list) {
display_message(Some(code), fmt, args);
}
/// See [`warnx`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn vwarnx(fmt: *const c_char, args: va_list) {
display_message(None, fmt, args);
}
// Write error messages for err and warn to the currently set sink.
unsafe fn display_message(code: Option<c_int>, fmt: *const c_char, args: va_list) {
// SAFETY:
// * error_sink is only null once on start but otherwise always stderr or a user set file
// * User is trusted to pass in a valid file pointer if err_set_file is used
if error_sink.is_null() {
error_sink = stdio::stderr;
}
let sink = error_sink;
// "progname:" is always printed
// SAFETY:
// * program_invocation_short_name is never null as it is set on start
// * program_invocation_short_name is not globally mutable so the user can't mangle it
fprintf(
sink,
c"%s".as_ptr(),
platform::program_invocation_short_name,
);
// Print user message if any
if !fmt.is_null() {
fputs(c": ".as_ptr(), sink);
vfprintf(sink, fmt, args);
}
// Print error message for non-x functions
if let Some(code) = code {
let message = strerror(code);
fprintf(sink, c": %s".as_ptr(), message);
}
// Always write new line
fputc(b'\n'.into(), sink);
}
// Write an error message as per err and then exit.
unsafe fn err_exit(eval: c_int, code: Option<c_int>, fmt: *const c_char, args: va_list) -> ! {
display_message(code, fmt, args);
if let Some(callback) = on_exit {
// errx will hit the unwrap.
callback(code.unwrap_or_else(|| ERRNO.get()));
}
exit(eval);
}
+9 -1
View File
@@ -1,4 +1,12 @@
sys_includes = ["bits/errno.h"]
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/errno.h.html
#
# There are no spec quotations relating to includes
sys_includes = []
after_includes = """
#define errno (*__errno_location())
#define program_invocation_name (*__program_invocation_name())
#define program_invocation_short_name (*__program_invocation_short_name())
"""
include_guard = "_RELIBC_ERRNO_H"
language = "C"
style = "Tag"
+65 -24
View File
@@ -1,38 +1,47 @@
//! errno implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/errno.h.html
//! `errno.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/errno.h.html>.
use crate::platform::{self, types::*};
use crate::platform::{
self,
types::{c_char, c_int},
};
//TODO: Consider removing, provided for compatibility with newlib
#[no_mangle]
pub unsafe extern "C" fn __errno() -> *mut c_int {
#[unsafe(no_mangle)]
pub extern "C" fn __errno() -> *mut c_int {
__errno_location()
}
#[no_mangle]
pub unsafe extern "C" fn __errno_location() -> *mut c_int {
&mut platform::errno
/// Provide a pointer to relibc's internal `errno` variable.
///
/// This is the basis of the C-facing macro definition of `errno`, and should
/// not be used directly.
#[unsafe(no_mangle)]
pub extern "C" fn __errno_location() -> *mut c_int {
platform::ERRNO.as_ptr()
}
#[no_mangle]
/// Get the name used to invoke the program, similar to `argv[0]`.
///
/// This provides the basis for the C-facing macro definition of
/// `program_invocation_name`, and should not be used directly.
///
/// The `program_invocation_name` variable is a GNU extension.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __program_invocation_name() -> *mut *mut c_char {
&mut platform::inner_argv[0]
&raw mut platform::program_invocation_name
}
#[no_mangle]
/// Get the directory-less name used to invoke the program.
///
/// This provides the basis for the C-facing macro definition of
/// `program_invocation_short_name`, and should not be used directly.
///
/// The `program_invocation_short_name` variable is a GNU extension.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __program_invocation_short_name() -> *mut *mut c_char {
let mut ptr = platform::inner_argv[0];
let mut slash_ptr = ptr;
loop {
let b = *ptr as u8;
if b == 0 {
return &mut slash_ptr;
} else {
ptr = ptr.add(1);
if b == b'/' {
slash_ptr = ptr;
}
}
}
&raw mut platform::program_invocation_short_name
}
pub const EPERM: c_int = 1; /* Operation not permitted */
@@ -130,6 +139,7 @@ pub const ENOPROTOOPT: c_int = 92; /* Protocol not available */
pub const EPROTONOSUPPORT: c_int = 93; /* Protocol not supported */
pub const ESOCKTNOSUPPORT: c_int = 94; /* Socket type not supported */
pub const EOPNOTSUPP: c_int = 95; /* Operation not supported on transport endpoint */
pub const ENOTSUP: c_int = EOPNOTSUPP; /* Not supported */
pub const EPFNOSUPPORT: c_int = 96; /* Protocol family not supported */
pub const EAFNOSUPPORT: c_int = 97; /* Address family not supported by protocol */
pub const EADDRINUSE: c_int = 98; /* Address already in use */
@@ -167,7 +177,8 @@ pub const EKEYREJECTED: c_int = 129; /* Key was rejected by service */
pub const EOWNERDEAD: c_int = 130; /* Owner died */
pub const ENOTRECOVERABLE: c_int = 131; /* State not recoverable */
pub static STR_ERROR: [&'static str; 132] = [
/// String representations for the respective `errno` values.
pub(crate) const STR_ERROR: [&str; 132] = [
"Success",
"Operation not permitted",
"No such file or directory",
@@ -301,3 +312,33 @@ pub static STR_ERROR: [&'static str; 132] = [
"Owner died",
"State not recoverable",
];
/// Longest error message length
pub(crate) const STRERROR_MAX: usize = {
// Number of digits of the max value of this platform's c_int
let digits = {
let mut len = 0;
let mut i = c_int::MAX;
while i > 0 {
i /= 10;
len += 1;
}
len
};
let mut longest: usize = "Unknown error: ".len() + digits + 1;
let mut i = 0;
while i < STR_ERROR.len() {
let len = STR_ERROR[i].len() + 1;
if len > longest {
longest = len;
}
i += 1;
}
longest
};
+1 -6
View File
@@ -1,14 +1,9 @@
sys_includes = ["stdarg.h", "sys/types.h"]
sys_includes = ["stdarg.h", "sys/stat.h", "unistd.h"]
include_guard = "_RELIBC_FCNTL_H"
trailer = "#include <bits/fcntl.h>"
language = "C"
style = "Tag"
no_includes = true
cpp_compat = true
[defines]
"target_os=linux" = "__linux__"
"target_os=redox" = "__redox__"
[enum]
prefix_with_name = true
+15 -1
View File
@@ -1,4 +1,4 @@
use crate::platform::types::*;
use crate::platform::types::c_int;
pub const O_RDONLY: c_int = 0x0000;
pub const O_WRONLY: c_int = 0x0001;
@@ -6,6 +6,7 @@ pub const O_RDWR: c_int = 0x0002;
pub const O_ACCMODE: c_int = 0x0003;
pub const O_CREAT: c_int = 0x0040;
pub const O_EXCL: c_int = 0x0080;
pub const O_NOCTTY: c_int = 0x0100;
pub const O_TRUNC: c_int = 0x0200;
pub const O_APPEND: c_int = 0x0400;
pub const O_NONBLOCK: c_int = 0x0800;
@@ -15,3 +16,16 @@ pub const O_CLOEXEC: c_int = 0x8_0000;
pub const O_PATH: c_int = 0x20_0000;
pub const FD_CLOEXEC: c_int = 0x8_0000;
// Defined for compatibility
pub const O_NDELAY: c_int = O_NONBLOCK;
// Flags for capability based "at" functions
pub const AT_FDCWD: c_int = -100;
pub const AT_SYMLINK_NOFOLLOW: c_int = 0x100;
// AT_EACCESS only used for faccessat
pub const AT_EACCESS: c_int = 0x200;
// AT_REMOVEDIR only used for unlinkat
pub const AT_REMOVEDIR: c_int = 0x200;
pub const AT_SYMLINK_FOLLOW: c_int = 0x400;
pub const AT_EMPTY_PATH: c_int = 0x1000;
+100 -13
View File
@@ -1,12 +1,23 @@
//! fcntl implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/fcntl.h.html
//! `fcntl.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fcntl.h.html>.
use core::num::NonZeroU64;
use crate::{
c_str::CStr,
platform::{types::*, Pal, Sys},
error::ResultExt,
header::unistd::close,
platform::{
Pal, Sys,
types::{c_char, c_int, c_short, c_ulonglong, mode_t, off_t, pid_t},
},
};
pub use self::sys::*;
use super::errno::EINVAL;
#[cfg(target_os = "linux")]
#[path = "linux.rs"]
pub mod sys;
@@ -23,16 +34,29 @@ pub const F_SETFL: c_int = 4;
pub const F_GETLK: c_int = 5;
pub const F_SETLK: c_int = 6;
pub const F_SETLKW: c_int = 7;
pub const F_OFD_GETLK: c_int = 36;
pub const F_OFD_SETLK: c_int = 37;
pub const F_OFD_SETLKW: c_int = 38;
pub const F_DUPFD_CLOEXEC: c_int = 1030;
pub const F_RDLCK: c_int = 0;
pub const F_WRLCK: c_int = 1;
pub const F_UNLCK: c_int = 2;
#[no_mangle]
pub const F_ULOCK: c_int = 0;
pub const F_LOCK: c_int = 1;
pub const F_TLOCK: c_int = 2;
pub const F_TEST: c_int = 3;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/creat.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn creat(path: *const c_char, mode: mode_t) -> c_int {
sys_open(path, O_WRONLY | O_CREAT | O_TRUNC, mode)
unsafe { open(path, O_WRONLY | O_CREAT | O_TRUNC, mode) }
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fcntl.h.html>.
#[repr(C)]
#[derive(Clone, Copy, Default)]
pub struct flock {
pub l_type: c_short,
pub l_whence: c_short,
@@ -40,16 +64,79 @@ pub struct flock {
pub l_len: off_t,
pub l_pid: pid_t,
}
#[no_mangle]
pub extern "C" fn sys_fcntl(fildes: c_int, cmd: c_int, arg: c_int) -> c_int {
Sys::fcntl(fildes, cmd, arg)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fcntl.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn fcntl(fildes: c_int, cmd: c_int, mut __valist: ...) -> c_int {
// c_ulonglong
let arg = match cmd {
F_DUPFD | F_SETFD | F_SETFL | F_GETLK | F_SETLK | F_SETLKW | F_OFD_GETLK | F_OFD_SETLK
| F_OFD_SETLKW | F_DUPFD_CLOEXEC => unsafe { __valist.next_arg::<c_ulonglong>() },
_ => 0,
};
if cmd == F_DUPFD_CLOEXEC {
let new_fd = Sys::fcntl(fildes, F_DUPFD_CLOEXEC, arg).or_minus_one_errno();
if new_fd >= 0 {
return new_fd;
}
let new_fd = Sys::fcntl(fildes, F_DUPFD, arg).or_minus_one_errno();
if new_fd < 0 {
return -1;
}
if Sys::fcntl(new_fd, F_SETFD, FD_CLOEXEC as c_ulonglong).or_minus_one_errno() < 0 {
let _ = close(new_fd);
return -1;
}
return new_fd;
}
Sys::fcntl(fildes, cmd, arg).or_minus_one_errno()
}
#[no_mangle]
pub unsafe extern "C" fn sys_open(path: *const c_char, oflag: c_int, mode: mode_t) -> c_int {
let path = CStr::from_ptr(path);
Sys::open(path, oflag, mode)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/open.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn open(path: *const c_char, oflag: c_int, mut __valist: ...) -> c_int {
unsafe { openat(AT_FDCWD, path, oflag, __valist) }
}
#[no_mangle]
pub unsafe extern "C" fn cbindgen_stupid_struct_user_for_fcntl(a: flock) {}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/openat.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn openat(
fd: c_int,
path: *const c_char,
oflag: c_int,
mut __valist: ...
) -> c_int {
let mode = if oflag & O_CREAT == O_CREAT
/* || oflag & O_TMPFILE == O_TMPFILE */
{
unsafe { __valist.next_arg::<mode_t>() }
} else {
0
};
let path = unsafe { CStr::from_ptr(path) };
Sys::openat(fd, path, oflag, mode).or_minus_one_errno()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cbindgen_stupid_struct_user_for_fcntl(_: flock) {}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_fallocate(fd: c_int, offset: off_t, length: off_t) -> c_int {
// Length can't be zero and offset must be positive.
let Ok(offset) = offset.try_into() else {
return EINVAL;
};
let Some(length) = length.try_into().ok().and_then(NonZeroU64::new) else {
return EINVAL;
};
// posix_fallocate does not set errno but instead returns it.
Sys::posix_fallocate(fd, offset, length)
.err()
.map(|e| e.0)
.unwrap_or_default()
}
+18 -1
View File
@@ -1,4 +1,4 @@
use crate::platform::types::*;
use crate::platform::types::c_int;
pub const O_RDONLY: c_int = 0x0001_0000;
pub const O_WRONLY: c_int = 0x0002_0000;
@@ -10,6 +10,7 @@ pub const O_SHLOCK: c_int = 0x0010_0000;
pub const O_EXLOCK: c_int = 0x0020_0000;
pub const O_ASYNC: c_int = 0x0040_0000;
pub const O_FSYNC: c_int = 0x0080_0000;
pub const O_SYNC: c_int = O_FSYNC;
pub const O_CLOEXEC: c_int = 0x0100_0000;
pub const O_CREAT: c_int = 0x0200_0000;
pub const O_TRUNC: c_int = 0x0400_0000;
@@ -21,3 +22,19 @@ pub const O_SYMLINK: c_int = 0x4000_0000;
pub const O_NOFOLLOW: c_int = -0x8000_0000;
pub const FD_CLOEXEC: c_int = 0x0100_0000;
pub const O_NOCTTY: c_int = 0x00000200;
// Defined for compatibility
pub const O_NDELAY: c_int = O_NONBLOCK;
// Flags for capability based "at" functions
pub const AT_FDCWD: c_int = -100;
pub const AT_SYMLINK_NOFOLLOW: c_int = 0x200;
pub const AT_REMOVEDIR: c_int = 0x200;
// Used by linkat()
pub const AT_SYMLINK_FOLLOW: c_int = 0x2000;
// nonstandard extension, but likely to be in a future standard
pub const AT_EMPTY_PATH: c_int = 0x4000;
// only used for faccessat()
pub const AT_EACCESS: c_int = 0x400;
+60 -1
View File
@@ -1,5 +1,64 @@
sys_includes = ["sys/types.h", "bits/float.h"]
sys_includes = ["sys/types.h"]
include_guard = "_RELIBC_FLOAT_H"
after_includes = """
#ifndef _RELIBC_BITS_FLOAT_H
#define _RELIBC_BITS_FLOAT_H
#define FLT_ROUNDS (flt_rounds())
// Shamelessly copy pasted from musl:
#define FLT_TRUE_MIN 1.40129846432481707092e-45F
#define FLT_MIN 1.17549435082228750797e-38F
#define FLT_MAX 3.40282346638528859812e+38F
#define FLT_EPSILON 1.1920928955078125e-07F
#define FLT_MANT_DIG 24
#define FLT_MIN_EXP (-125)
#define FLT_MAX_EXP 128
#define FLT_HAS_SUBNORM 1
#define FLT_DIG 6
#define FLT_DECIMAL_DIG 9
#define FLT_MIN_10_EXP (-37)
#define FLT_MAX_10_EXP 38
#define DBL_TRUE_MIN 4.94065645841246544177e-324
#define DBL_MIN 2.22507385850720138309e-308
#define DBL_MAX 1.79769313486231570815e+308
#define DBL_EPSILON 2.22044604925031308085e-16
#define DBL_MANT_DIG 53
#define DBL_MIN_EXP (-1021)
#define DBL_MAX_EXP 1024
#define DBL_HAS_SUBNORM 1
#define DBL_DIG 15
#define DBL_DECIMAL_DIG 17
#define DBL_MIN_10_EXP (-307)
#define DBL_MAX_10_EXP 308
#define LDBL_HAS_SUBNORM 1
#define LDBL_DECIMAL_DIG DECIMAL_DIG
#define FLT_EVAL_METHOD 0
#define DECIMAL_DIG 21
// TODO: Support more architectures than x86_64 here:
#define LDBL_TRUE_MIN 3.6451995318824746025e-4951L
#define LDBL_MIN 3.3621031431120935063e-4932L
#define LDBL_MAX 1.1897314953572317650e+4932L
#define LDBL_EPSILON 1.0842021724855044340e-19L
#define LDBL_MANT_DIG 64
#define LDBL_MIN_EXP (-16381)
#define LDBL_MAX_EXP 16384
#define LDBL_DIG 18
#define LDBL_MIN_10_EXP (-4931)
#define LDBL_MAX_10_EXP 4932
#endif // _RELIBC_BITS_FLOAT_H
"""
language = "C"
style = "Tag"
no_includes = true
+9 -6
View File
@@ -1,16 +1,19 @@
//! float.h implementation for Redox, following
//! http://pubs.opengroup.org/onlinepubs/7908799/xsh/float.h.html
//! `float.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/float.h.html>.
use crate::{
header::_fenv::{fegetround, FE_TONEAREST},
platform::types::*,
header::_fenv::{FE_TONEAREST, fegetround},
platform::types::c_int,
};
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/float.h.html>.
pub const FLT_RADIX: c_int = 2;
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/float.h.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn flt_rounds() -> c_int {
match fegetround() {
match unsafe { fegetround() } {
FE_TONEAREST => 1,
_ => -1,
}
+3
View File
@@ -1,3 +1,6 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fnmatch.h.html
#
# There are no spec quotations relating to includes
include_guard = "_RELIBC_FNMATCH_H"
language = "C"
style = "Tag"
+46 -28
View File
@@ -1,12 +1,15 @@
//! fnmatch implementation
//! `fnmatch.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fnmatch.h.html>.
use alloc::{borrow::Cow, vec::Vec};
use core::slice;
use crate::platform::types::*;
use crate::platform::types::{c_char, c_int};
use posix_regex::{
compile::{Collation, Range, Token},
PosixRegex,
compile::{Collation, Range, Token},
tree::{Tree, TreeBuilder},
};
const ONCE: Range = Range(1, Some(1));
@@ -17,9 +20,10 @@ pub const FNM_NOESCAPE: c_int = 1;
pub const FNM_PATHNAME: c_int = 2;
pub const FNM_PERIOD: c_int = 4;
pub const FNM_CASEFOLD: c_int = 8;
pub const FNM_IGNORECASE: c_int = FNM_CASEFOLD;
// TODO: FNM_EXTMATCH
unsafe fn tokenize(mut pattern: *const u8, flags: c_int) -> Vec<(Token, Range)> {
unsafe fn tokenize(mut pattern: *const u8, flags: c_int) -> Tree {
fn any(leading: bool, flags: c_int) -> Token {
let mut list = Vec::new();
if flags & FNM_PATHNAME == FNM_PATHNAME {
@@ -38,24 +42,34 @@ unsafe fn tokenize(mut pattern: *const u8, flags: c_int) -> Vec<(Token, Range)>
c == b'/' && flags & FNM_PATHNAME == FNM_PATHNAME
}
let mut tokens = Vec::new();
let mut leading = true;
let mut need_collapsing = false;
while *pattern != 0 {
let mut builder = TreeBuilder::default();
builder.start_internal(Token::Root, Range(1, Some(1)));
builder.start_internal(Token::Alternative, Range(1, Some(1)));
while unsafe { *pattern != 0 } {
let was_leading = leading;
leading = false;
let c = *pattern;
pattern = pattern.offset(1);
let c = unsafe { *pattern };
pattern = unsafe { pattern.offset(1) };
tokens.push(match c {
match (c == b'*', need_collapsing) {
(true, true) => continue,
(true, false) => need_collapsing = true,
(false, _) => need_collapsing = false,
}
let (token, range) = match c {
b'\\' if flags & FNM_NOESCAPE == FNM_NOESCAPE => {
let c = *pattern;
let c = unsafe { *pattern };
if c == 0 {
// Trailing backslash. Maybe error here?
break;
}
pattern = pattern.offset(1);
pattern = unsafe { pattern.offset(1) };
leading = is_leading(flags, c);
(Token::Char(c), ONCE)
}
@@ -63,24 +77,24 @@ unsafe fn tokenize(mut pattern: *const u8, flags: c_int) -> Vec<(Token, Range)>
b'*' => (any(was_leading, flags), Range(0, None)),
b'[' => {
let mut list: Vec<Collation> = Vec::new();
let invert = if *pattern == b'!' {
pattern = pattern.offset(1);
let invert = if unsafe { *pattern == b'!' } {
pattern = unsafe { pattern.offset(1) };
true
} else {
false
};
loop {
let mut c = *pattern;
let mut c = unsafe { *pattern };
if c == 0 {
break;
}
pattern = pattern.offset(1);
pattern = unsafe { pattern.offset(1) };
match c {
b']' => break,
b'\\' => {
c = *pattern;
pattern = pattern.offset(1);
c = unsafe { *pattern };
pattern = unsafe { pattern.offset(1) };
if c == 0 {
// Trailing backslash. Maybe error?
break;
@@ -88,9 +102,9 @@ unsafe fn tokenize(mut pattern: *const u8, flags: c_int) -> Vec<(Token, Range)>
}
_ => (),
}
if *pattern == b'-' && *pattern.offset(1) != 0 {
let end = *pattern.offset(1);
pattern = pattern.offset(2);
if unsafe { *pattern == b'-' && *pattern.offset(1) != 0 } {
let end = unsafe { *pattern.offset(1) };
pattern = unsafe { pattern.offset(2) };
for c in c..=end {
if can_push(was_leading, flags, c) {
list.push(Collation::Char(c));
@@ -108,12 +122,17 @@ unsafe fn tokenize(mut pattern: *const u8, flags: c_int) -> Vec<(Token, Range)>
leading = is_leading(flags, c);
(Token::Char(c), ONCE)
}
})
};
builder.leaf(token, range);
}
tokens
builder.leaf(Token::End, ONCE);
builder.finish_internal();
builder.finish_internal();
builder.finish()
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fnmatch.html>.
#[unsafe(no_mangle)]
#[linkage = "weak"] // often redefined in GNU programs
pub unsafe extern "C" fn fnmatch(
pattern: *const c_char,
@@ -121,15 +140,14 @@ pub unsafe extern "C" fn fnmatch(
flags: c_int,
) -> c_int {
let mut len = 0;
while *input.offset(len) != 0 {
while unsafe { *input.offset(len) != 0 } {
len += 1;
}
let input = slice::from_raw_parts(input as *const u8, len as usize);
let input = unsafe { slice::from_raw_parts(input.cast::<u8>(), len as usize) };
let mut tokens = tokenize(pattern as *const u8, flags);
tokens.push((Token::End, ONCE));
let tokens = unsafe { tokenize(pattern.cast::<u8>(), flags) };
if PosixRegex::new(Cow::Owned(vec![tokens]))
if PosixRegex::new(Cow::Owned(tokens))
.case_insensitive(flags & FNM_CASEFOLD == FNM_CASEFOLD)
.matches_exact(input)
.is_some()
+98 -72
View File
@@ -1,20 +1,27 @@
//! getopt implementation for relibc
//! `getopt.h` implementation.
//!
//! Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getopt.3.html>.
use crate::{
header::{
stdio, string,
unistd::{optarg, opterr, optind, optopt},
},
platform::types::*,
platform::types::{c_char, c_int, size_t},
};
use core::ptr;
/// cbindgen:ignore
static mut CURRENT_OPT: *mut c_char = ptr::null_mut();
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getopt.3.html>.
pub const no_argument: c_int = 0;
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getopt.3.html>.
pub const required_argument: c_int = 1;
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getopt.3.html>.
pub const optional_argument: c_int = 2;
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getopt.3.html>.
#[repr(C)]
pub struct option {
name: *const c_char,
@@ -23,8 +30,10 @@ pub struct option {
val: c_int,
}
#[no_mangle]
#[linkage = "weak"] // often redefined in GNU programs
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getopt.3.html>.
///
/// Functions the same as `getopt` but also accepts long options.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getopt_long(
argc: c_int,
argv: *const *mut c_char,
@@ -33,94 +42,109 @@ pub unsafe extern "C" fn getopt_long(
longindex: *mut c_int,
) -> c_int {
// if optarg is not set, we still don't want the previous value leaking
optarg = ptr::null_mut();
// handle reinitialization request
if optind == 0 {
optind = 1;
CURRENT_OPT = ptr::null_mut();
unsafe {
optarg = ptr::null_mut();
}
if CURRENT_OPT.is_null() || *CURRENT_OPT == 0 {
if optind >= argc {
// handle reinitialization request
unsafe {
if optind == 0 {
optind = 1;
CURRENT_OPT = ptr::null_mut();
}
}
if unsafe { CURRENT_OPT.is_null() || *CURRENT_OPT == 0 } {
if unsafe { optind >= argc } {
-1
} else {
let current_arg = *argv.offset(optind as isize);
if current_arg.is_null()
|| *current_arg != b'-' as c_char
|| *current_arg.offset(1) == 0
{
let current_arg = unsafe { *argv.offset(optind as isize) };
if unsafe {
current_arg.is_null()
|| *current_arg != b'-' as c_char
|| *current_arg.offset(1) == 0
} {
-1
} else if string::strcmp(current_arg, c_str!("--").as_ptr()) == 0 {
optind += 1;
} else if unsafe { string::strcmp(current_arg, c"--".as_ptr()) == 0 } {
unsafe {
optind += 1;
}
-1
} else {
// remove the '-'
let current_arg = current_arg.offset(1);
let current_arg = unsafe { current_arg.offset(1) };
if *current_arg == b'-' as c_char && !longopts.is_null() {
let current_arg = current_arg.offset(1);
if unsafe { *current_arg == b'-' as c_char } && !longopts.is_null() {
let current_arg = unsafe { current_arg.offset(1) };
// is a long option
for i in 0.. {
let opt = &*longopts.offset(i);
let opt = unsafe { &*longopts.offset(i) };
if opt.name.is_null() {
break;
}
let mut end = 0;
while {
let c = *current_arg.offset(end);
let c = unsafe { *current_arg.offset(end) };
c != 0 && c != b'=' as c_char
} {
end += 1;
}
if string::strncmp(current_arg, opt.name, end as size_t) == 0 {
optind += 1;
*longindex = i as c_int;
if unsafe { string::strncmp(current_arg, opt.name, end as size_t) == 0 } {
unsafe {
optind += 1;
if !longindex.is_null() {
*longindex = i as c_int;
}
}
if opt.has_arg == optional_argument {
if *current_arg.offset(end) == b'=' as c_char {
optarg = current_arg.offset(end + 1);
unsafe {
if *current_arg.offset(end) == b'=' as c_char {
optarg = current_arg.offset(end + 1);
}
}
} else if opt.has_arg == required_argument {
if *current_arg.offset(end) == b'=' as c_char {
optarg = current_arg.offset(end + 1);
} else if optind < argc {
optarg = *argv.offset(optind as isize);
optind += 1;
} else if *optstring == b':' as c_char {
return b':' as c_int;
} else {
stdio::fputs(*argv as _, &mut *stdio::stderr);
stdio::fputs(
": option '--\0".as_ptr() as _,
&mut *stdio::stderr,
);
stdio::fputs(current_arg, &mut *stdio::stderr);
stdio::fputs(
"' requires an argument\n\0".as_ptr() as _,
&mut *stdio::stderr,
);
return b'?' as c_int;
unsafe {
if *current_arg.offset(end) == b'=' as c_char {
optarg = current_arg.offset(end + 1);
} else if optind < argc {
optarg = *argv.offset(optind as isize);
optind += 1;
} else if *optstring == b':' as c_char {
return c_int::from(b':');
} else {
stdio::fputs((*argv).cast_const(), &raw mut *stdio::stderr);
stdio::fputs(
c": option '--".as_ptr().cast(),
&raw mut *stdio::stderr,
);
stdio::fputs(current_arg, &raw mut *stdio::stderr);
stdio::fputs(
c"' requires an argument\n".as_ptr().cast(),
&raw mut *stdio::stderr,
);
return c_int::from(b'?');
}
}
}
if opt.flag.is_null() {
return opt.val;
} else {
*opt.flag = opt.val;
unsafe { *opt.flag = opt.val };
return 0;
}
}
}
}
parse_arg(argc, argv, current_arg, optstring)
unsafe { parse_arg(argc, argv, current_arg, optstring) }
}
}
} else {
parse_arg(argc, argv, CURRENT_OPT, optstring)
unsafe { parse_arg(argc, argv, CURRENT_OPT, optstring) }
}
}
@@ -130,35 +154,35 @@ unsafe fn parse_arg(
current_arg: *mut c_char,
optstring: *const c_char,
) -> c_int {
let update_current_opt = || {
let update_current_opt = || unsafe {
CURRENT_OPT = current_arg.offset(1);
if *CURRENT_OPT == 0 {
optind += 1;
}
};
let print_error = |desc: &[u8]| {
let print_error = |desc: &[u8]| unsafe {
// NOTE: we don't use fprintf to get around the usage of va_list
stdio::fputs(*argv as _, &mut *stdio::stderr);
stdio::fputs(desc.as_ptr() as _, &mut *stdio::stderr);
stdio::fputc(*current_arg as _, &mut *stdio::stderr);
stdio::fputc(b'\n' as _, &mut *stdio::stderr);
stdio::fputs((*argv).cast_const(), &raw mut *stdio::stderr);
stdio::fputs(desc.as_ptr().cast(), &raw mut *stdio::stderr);
stdio::fputc((*current_arg).into(), &raw mut *stdio::stderr);
stdio::fputc(b'\n'.into(), &raw mut *stdio::stderr);
};
match find_option(*current_arg, optstring) {
match unsafe { find_option(*current_arg, optstring) } {
Some(GetoptOption::Flag) => {
update_current_opt();
*current_arg as c_int
unsafe { c_int::from(*current_arg) }
}
Some(GetoptOption::OptArg) => {
CURRENT_OPT = b"\0".as_ptr() as _;
Some(GetoptOption::OptArg) => unsafe {
CURRENT_OPT = c"".as_ptr().cast_mut();
if *current_arg.offset(1) == 0 {
optind += 2;
if optind > argc {
CURRENT_OPT = ptr::null_mut();
optopt = *current_arg as c_int;
optopt = c_int::from(*current_arg);
let errch = if *optstring == b':' as c_char {
b':'
} else {
@@ -168,29 +192,31 @@ unsafe fn parse_arg(
b'?'
};
errch as c_int
c_int::from(errch)
} else {
optarg = *argv.offset(optind as isize - 1);
*current_arg as c_int
c_int::from(*current_arg)
}
} else {
optarg = current_arg.offset(1);
optind += 1;
*current_arg as c_int
c_int::from(*current_arg)
}
}
},
None => {
// couldn't find the given option in optstring
if opterr != 0 {
if unsafe { opterr != 0 } {
print_error(b": illegal option -- \0");
}
update_current_opt();
optopt = *current_arg as c_int;
b'?' as c_int
unsafe {
optopt = c_int::from(*current_arg);
}
c_int::from(b'?')
}
}
}
@@ -203,9 +229,9 @@ enum GetoptOption {
unsafe fn find_option(ch: c_char, optstring: *const c_char) -> Option<GetoptOption> {
let mut i = 0;
while *optstring.offset(i) != 0 {
if *optstring.offset(i) == ch {
let result = if *optstring.offset(i + 1) == b':' as c_char {
while unsafe { *optstring.offset(i) != 0 } {
if unsafe { *optstring.offset(i) == ch } {
let result = if unsafe { *optstring.offset(i + 1) == b':' as c_char } {
GetoptOption::OptArg
} else {
GetoptOption::Flag
+16
View File
@@ -0,0 +1,16 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/glob.h.html
#
# Spec quotations relating to includes:
# - "The <glob.h> header shall define the size_t type as described in <sys/types.h>."
#
# size_t is defined in stddef.h (sys/types.h gets size_t by including stddef.h)
# just use stddef.h to avoid importing all of sys/types.h
sys_includes = ["stddef.h"]
include_guard = "_RELIBC_GLOB_H"
language = "C"
style = "type"
no_includes = true
cpp_compat = true
[enum]
prefix_with_name = true
+401
View File
@@ -0,0 +1,401 @@
//! `glob.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/glob.h.html>.
use core::ptr;
use alloc::{boxed::Box, vec::Vec};
use crate::{
c_str::{CStr, CString},
header::{
dirent::{closedir, opendir, readdir},
errno::*,
fnmatch::{FNM_NOESCAPE, FNM_PERIOD, fnmatch},
sys_stat::{S_IFDIR, S_IFMT, stat},
},
platform::{
self,
types::{c_char, c_int, c_uchar, c_void, size_t},
},
};
// Cause glob() to return on error
pub const GLOB_ERR: c_int = 0x0001;
// Each pathname that is a directory that matches pattern has a slash appended
pub const GLOB_MARK: c_int = 0x0002;
// Do not sort returned pathnames
pub const GLOB_NOSORT: c_int = 0x0004;
// Add gl_offs amount of null pointers to the beginning of `gl_pathv`
pub const GLOB_DOOFFS: c_int = 0x0008;
// If pattern does not match, return a list containing only pattern
pub const GLOB_NOCHECK: c_int = 0x0010;
// Append generated pathnames to those previously obtained
pub const GLOB_APPEND: c_int = 0x0020;
// Disable backslash escaping
pub const GLOB_NOESCAPE: c_int = 0x0040;
// Allow wildcards to match '.' (GNU extension)
pub const GLOB_PERIOD: c_int = 0x0080;
// Attempt to allocate memory failed
pub const GLOB_NOSPACE: c_int = 1;
// Scan was stopped because GLOB_ERR was set or `errfunc` returned non-zero
pub const GLOB_ABORTED: c_int = 2;
// Pattern does not match any existing pathname, and GLOB_NOCHECK was not set
pub const GLOB_NOMATCH: c_int = 3;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/glob.h.html>.
#[derive(Debug)]
#[repr(C)]
pub struct glob_t {
pub gl_pathc: size_t, // Count of paths matched by pattern (POSIX required field)
pub gl_offs: size_t, // Slots to reserve at the beginning of gl_pathv (POSIX required field)
pub gl_pathv: *mut *mut c_char, // Pointer to list of matched pathnames (POSIX required field)
// Opaque pointer to allocation data
__opaque: *mut c_void, // Vec<*mut c_char>
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/glob.html>.
#[linkage = "weak"] // GNU prefers its own glob e.g. in Make
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glob(
pattern: *const c_char,
flags: c_int,
errfunc: Option<unsafe extern "C" fn(epath: *const c_char, eerrno: c_int) -> c_int>,
pglob: *mut glob_t,
) -> c_int {
if flags & GLOB_APPEND != GLOB_APPEND {
unsafe {
(*pglob).gl_pathc = 0;
(*pglob).gl_pathv = ptr::null_mut();
(*pglob).__opaque = ptr::null_mut();
}
}
let glob_expr = unsafe { CStr::from_ptr(pattern) };
if glob_expr.to_bytes() == b"" {
return GLOB_NOMATCH;
}
let base_path = unsafe {
CStr::from_bytes_with_nul_unchecked(if glob_expr.to_bytes().first() == Some(&b'/') {
b"/\0"
} else {
b"\0"
})
};
let errfunc = match errfunc {
Some(f) => f,
None => default_errfunc,
};
// Do the globbing
let mut results = match inner_glob(&base_path, &glob_expr, flags, errfunc) {
Ok(res) => res,
Err(e) => return e,
};
// Handle GLOB_NOCHECK and no matches
if results.is_empty() {
if flags & GLOB_NOCHECK == GLOB_NOCHECK {
results.push(glob_expr.to_owned_cstring());
} else {
return GLOB_NOMATCH;
}
}
// Handle GLOB_NOSORT
if flags & GLOB_NOSORT != GLOB_NOSORT {
results.sort();
}
// Set gl_pathc
if flags & GLOB_APPEND == GLOB_APPEND {
unsafe {
(*pglob).gl_pathc += results.len();
}
} else {
unsafe {
(*pglob).gl_pathc = results.len();
}
}
let mut pathv: Box<Vec<*mut c_char>>;
if flags & GLOB_APPEND == GLOB_APPEND {
pathv = unsafe { Box::from_raw((*pglob).__opaque.cast()) };
pathv.pop(); // Remove NULL from end
} else {
pathv = Box::new(Vec::new());
if flags & GLOB_DOOFFS == GLOB_DOOFFS {
let gl_offs = unsafe { (*pglob).gl_offs };
pathv.reserve(gl_offs);
for _ in 0..gl_offs {
pathv.push(ptr::null_mut());
}
}
}
pathv.reserve_exact(results.len() + 1);
pathv.extend(results.into_iter().map(|s| s.into_raw()));
pathv.push(ptr::null_mut());
unsafe {
(*pglob).gl_pathv = pathv.as_ptr().cast_mut();
(*pglob).__opaque = Box::into_raw(pathv).cast();
}
0
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/glob.html>.
#[linkage = "weak"] // GNU prefers its own glob e.g. in Make
#[unsafe(no_mangle)]
pub unsafe extern "C" fn globfree(pglob: *mut glob_t) {
// Retake ownership
if unsafe { !(*pglob).__opaque.is_null() } {
let pathv: Box<Vec<*mut c_char>> = unsafe { Box::from_raw((*pglob).__opaque.cast()) };
for (idx, path) in pathv.into_iter().enumerate() {
if unsafe { idx < (*pglob).gl_offs } {
continue;
}
if !path.is_null() {
unsafe {
drop(CString::from_raw(path));
}
}
}
unsafe {
(*pglob).gl_pathv = ptr::null_mut();
}
}
}
type GlobErrorFunc = unsafe extern "C" fn(epath: *const c_char, eerrno: c_int) -> c_int;
struct DirEntry {
name: CString,
is_dir: bool,
}
unsafe extern "C" fn default_errfunc(epath: *const c_char, eerrno: c_int) -> c_int {
0
}
fn list_dir(
path: &CStr,
errfunc: GlobErrorFunc,
abort_on_error: bool,
) -> Result<Vec<DirEntry>, c_int> {
const DT_DIR: c_uchar = 4; // From dirent.h
const DT_LNK: c_uchar = 10; // From dirent.h
let old_errno = platform::ERRNO.get();
let mut results: Vec<DirEntry> = Vec::new();
let open_path = if path.to_bytes().is_empty() {
unsafe { &CStr::from_bytes_with_nul_unchecked(b".\0") }
} else {
path
};
let dir = unsafe { opendir(open_path.as_ptr()) };
if dir.is_null() {
let new_errno = platform::ERRNO.get();
platform::ERRNO.set(old_errno);
if unsafe { errfunc(path.as_ptr(), new_errno) } != 0 || abort_on_error {
return Err(GLOB_ABORTED);
}
return Ok(results);
}
platform::ERRNO.set(0);
loop {
let entry = unsafe { readdir(&mut *dir) };
if entry.is_null() {
break;
}
let name = unsafe { CStr::from_ptr((*entry).d_name.as_ptr()).to_owned_cstring() };
if name.as_bytes() == b"." || name.as_bytes() == b".." {
continue;
}
let is_dir: bool = unsafe {
if (*entry).d_type == DT_DIR {
true
} else if (*entry).d_type == DT_LNK {
// Resolve symbolic link
let mut full_path = path.to_owned_cstring().into_string().unwrap();
if !full_path.ends_with('/') {
full_path.push('/');
}
full_path.push_str(name.to_str().unwrap());
full_path.push('\0');
let mut link_info = stat::default();
if stat(
full_path.as_ptr().cast::<c_char>(),
ptr::from_mut(&mut link_info),
) != 0
{
let errno = platform::ERRNO.get();
platform::ERRNO.set(old_errno);
if errfunc(full_path.as_ptr().cast::<c_char>(), errno) != 0 || abort_on_error {
return Err(GLOB_ABORTED);
}
}
link_info.st_mode & S_IFMT == S_IFDIR
} else {
false
}
};
results.push(DirEntry { name, is_dir });
}
// Check if entry == NULL because of an error
let errno = platform::ERRNO.get();
unsafe { closedir(Box::from_raw(dir)) };
// Restore the old errno
platform::ERRNO.set(old_errno);
if errno != 0 && (unsafe { errfunc(path.as_ptr(), errno) } != 0 || abort_on_error) {
return Err(GLOB_ABORTED);
}
Ok(results)
}
fn inner_glob(
current_dir: &CStr,
glob_expr: &CStr,
flags: c_int,
errfunc: GlobErrorFunc,
) -> Result<Vec<CString>, c_int> {
let mut pattern: Vec<u8> = Vec::new();
// Remove any '/' chars at the start of the expression
let glob_expr = {
let mut expr = glob_expr.to_bytes_with_nul();
while expr.first() == Some(&b'/') {
expr = &expr[1..];
}
unsafe { CStr::from_bytes_with_nul_unchecked(expr) }
};
// Get the next section of the glob expression (up to non-escaped '/')
let glob_iter = glob_expr.to_bytes();
let mut in_bracket = false;
let mut escaped = false;
let mut glob_consumed = 0;
for ch in glob_iter {
// Don't consume nul
if ch == &b'\0' {
break;
}
glob_consumed += 1;
if ch == &b'/' && !escaped {
break;
}
if ch == &b'[' && !escaped {
in_bracket = true;
} else if ch == &b']' {
// '\' is a normal character in brackets so doesn't escape
in_bracket = false;
}
escaped =
ch == &b'\\' && !in_bracket && !escaped && (flags & GLOB_NOESCAPE != GLOB_NOESCAPE);
pattern.push(*ch);
}
// Needs to be C-string
pattern.push(b'\0');
let new_glob_expr = unsafe {
CStr::from_bytes_with_nul_unchecked(&glob_expr.to_bytes_with_nul()[glob_consumed..])
};
// Handle special path sections
if pattern == b".\0" || pattern == b"..\0" {
let mut new_dir: Vec<u8> = Vec::new();
new_dir.extend_from_slice(current_dir.to_bytes());
new_dir.extend_from_slice(&pattern);
let new_dir_c = unsafe { CStr::from_bytes_with_nul_unchecked(&new_dir) };
return inner_glob(&new_dir_c, &new_glob_expr, flags, errfunc);
}
let mut fnmatch_flags = 0;
if flags & GLOB_NOESCAPE == GLOB_NOESCAPE {
fnmatch_flags |= FNM_NOESCAPE;
}
if flags & GLOB_PERIOD == GLOB_PERIOD {
fnmatch_flags |= FNM_PERIOD;
}
let mut matches: Vec<CString> = Vec::new();
for entry in list_dir(current_dir, errfunc, flags & GLOB_ERR == GLOB_ERR)? {
// If we still have pattern to match ignore non-directories
if !new_glob_expr.to_bytes().is_empty() && !entry.is_dir {
continue;
}
let mut path = current_dir.to_bytes().to_vec();
if path != b"" && !path.ends_with(b"/") {
path.push(b'/');
}
path.extend_from_slice(entry.name.as_bytes());
if flags & GLOB_MARK == GLOB_MARK && new_glob_expr.to_bytes() == b"" && entry.is_dir {
path.push(b'/');
}
// This shouldn't ever panic, we know the vec has no nul bytes
let path = CString::new(path).unwrap();
if unsafe {
fnmatch(
pattern.as_ptr().cast::<c_char>(),
entry.name.as_ptr(),
fnmatch_flags,
)
} == 0
{
if entry.is_dir && new_glob_expr.to_bytes() != b"" {
let new_matches = inner_glob(&CStr::borrow(&path), &new_glob_expr, flags, errfunc)?;
matches.extend(new_matches);
} else {
matches.push(path);
}
}
}
// It is an error if we don't find a directory when we expect one
if matches.is_empty() && !new_glob_expr.to_bytes().is_empty() {
let mut path = current_dir.to_bytes().to_vec();
path.extend_from_slice(&pattern);
if unsafe { errfunc(path.as_ptr().cast::<c_char>(), ENOENT) } != 0
|| flags & GLOB_ERR == GLOB_ERR
{
return Err(GLOB_ABORTED);
}
}
Ok(matches)
}
+7 -2
View File
@@ -1,8 +1,13 @@
sys_includes = []
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/grp.h.html
#
# Spec quotations relating to includes:
# - "The <grp.h> header shall define the gid_t and size_t types as described in <sys/types.h>."
sys_includes = ["sys/types.h"]
include_guard = "_RELIBC_GRP_H"
language = "C"
style = "Tag"
no_includes = true
usize_is_size_t = true
no_includes = false
cpp_compat = true
[enum]
+525 -32
View File
@@ -1,8 +1,91 @@
//! grp implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/grp.h.html
//! `grp.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/grp.h.html>.
use crate::platform::types::*;
use core::{
cell::SyncUnsafeCell,
convert::TryInto,
mem::{self, MaybeUninit},
num::ParseIntError,
ops::{Deref, DerefMut},
pin::Pin,
ptr, slice,
};
use alloc::{
borrow::ToOwned,
string::{FromUtf8Error, String},
};
use crate::{
error::ResultExt,
fs::File,
header::{errno, fcntl, limits, string::strlen},
io::{self, BufReader, Lines, prelude::*},
platform::{
self, Pal, Sys,
types::{c_char, c_int, c_void, gid_t, size_t},
},
};
use super::{errno::*, string::strncmp};
#[cfg(target_os = "linux")]
const SEPARATOR: char = ':';
#[cfg(target_os = "redox")]
const SEPARATOR: char = ';';
const GROUP_FILE: &core::ffi::CStr = c"/etc/group";
#[derive(Clone, Copy, Debug)]
struct DestBuffer {
ptr: *mut u8,
len: usize,
}
// Shamelessly stolen from pwd/mod.rs
#[derive(Debug)]
enum MaybeAllocated {
Owned(Pin<Box<[u8]>>),
Borrowed(DestBuffer),
}
impl Deref for MaybeAllocated {
type Target = [u8];
fn deref(&self) -> &Self::Target {
match self {
MaybeAllocated::Owned(boxed) => boxed,
MaybeAllocated::Borrowed(dst) => unsafe {
core::slice::from_raw_parts(dst.ptr, dst.len)
},
}
}
}
impl DerefMut for MaybeAllocated {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
MaybeAllocated::Owned(boxed) => boxed,
MaybeAllocated::Borrowed(dst) => unsafe {
core::slice::from_raw_parts_mut(dst.ptr, dst.len)
},
}
}
}
static mut GROUP_BUF: Option<MaybeAllocated> = None;
static mut GROUP: group = group {
gr_name: ptr::null_mut(),
gr_passwd: ptr::null_mut(),
gr_gid: 0,
gr_mem: ptr::null_mut(),
};
static LINE_READER: SyncUnsafeCell<Option<Lines<BufReader<File>>>> = SyncUnsafeCell::new(None);
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/grp.h.html>.
#[repr(C)]
#[derive(Debug)]
pub struct group {
pub gr_name: *mut c_char,
pub gr_passwd: *mut c_char,
@@ -10,56 +93,466 @@ pub struct group {
pub gr_mem: *mut *mut c_char,
}
// #[no_mangle]
pub extern "C" fn getgrgid(gid: gid_t) -> *mut group {
unimplemented!();
#[derive(Debug)]
enum Error {
EOF,
SyntaxError,
BufTooSmall,
Misc(io::Error),
FromUtf8Error(FromUtf8Error),
ParseIntError(ParseIntError),
Other,
}
// #[no_mangle]
pub extern "C" fn getgrnam(name: *const c_char) -> *mut group {
unimplemented!();
#[derive(Debug)]
struct OwnedGrp {
buffer: MaybeAllocated,
reference: group,
}
// #[no_mangle]
pub extern "C" fn getgrgid_r(
impl OwnedGrp {
fn into_global(self) -> *mut group {
unsafe {
GROUP_BUF = Some(self.buffer);
GROUP = self.reference;
&raw mut GROUP
}
}
}
fn split(buf: &mut [u8]) -> Option<group> {
let gr_gid = match buf[0..mem::size_of::<gid_t>()].try_into() {
Ok(buf) => gid_t::from_ne_bytes(buf),
Err(err) => return None,
};
// Get address of buffer for fixing up gr_mem
let buf_addr = buf.as_ptr() as usize;
// We moved the gid to the beginning of the byte buffer so we can do this.
let mut parts = buf[mem::size_of::<gid_t>()..].split_mut(|&c| c == b'\0');
let gr_name = parts.next()?.as_mut_ptr().cast::<c_char>();
let gr_passwd = parts.next()?.as_mut_ptr().cast::<c_char>();
let gr_mem = parts.next()?.as_mut_ptr().cast::<usize>();
// Adjust gr_mem address by buffer base address
// TODO: max group members length?
for i in 0..4096 {
unsafe {
if *gr_mem.add(i) == 0 {
// End of gr_mem pointer array
break;
}
*gr_mem.add(i) += buf_addr;
}
}
Some(group {
gr_name,
gr_passwd,
gr_gid,
gr_mem: gr_mem.cast::<*mut c_char>(),
})
}
fn parse_grp(line: String, destbuf: Option<DestBuffer>) -> Result<OwnedGrp, Error> {
let buffer = line.to_owned().into_bytes();
let mut buffer = buffer
.into_iter()
.map(|i| if i == SEPARATOR as u8 { b'\0' } else { i })
.chain([b'\0'])
.collect::<Vec<_>>();
let mut buffer = buffer.split_mut(|i| *i == b'\0');
let strings = {
let mut vec: Vec<u8> = Vec::new();
let gr_name = buffer.next().ok_or(Error::EOF)?.to_vec();
let gr_passwd = buffer.next().ok_or(Error::EOF)?.to_vec();
let gr_gid = String::from_utf8(buffer.next().ok_or(Error::EOF)?.to_vec())
.map_err(Error::FromUtf8Error)?
.parse::<gid_t>()
.map_err(Error::ParseIntError)?;
// Place the gid at the beginning of the byte buffer to make getting it back out again later, much faster.
vec.extend(gr_gid.to_ne_bytes());
vec.extend(gr_name);
vec.push(0);
vec.extend(gr_passwd);
vec.push(0);
let members = buffer.next().ok_or(Error::EOF)?;
// Get the offset of the members array
let member_array_start = vec.len();
// Push enough null pointers to fit all members
for _member in members
.split(|b| *b == b',')
.filter(|member| !member.is_empty())
{
vec.extend(0usize.to_ne_bytes());
}
let member_array_end = vec.len();
// Push a null pointer to terminate the members array
vec.extend(0usize.to_ne_bytes());
// Fill in member names
for (i, member) in members
.split(|b| *b == b',')
.filter(|member| !member.is_empty())
.enumerate()
{
let cur_offset = vec.len();
// This must be recomputed each time, because `vec` is undergoing extensions and so
// its backing memory might be reallocated and moved and its old memory deallocated.
let member_array = &mut vec[member_array_start..member_array_end];
let member_ptr = {
const SIZEOF_PTR: usize = mem::size_of::<*mut c_void>();
let start = i * SIZEOF_PTR;
let end = start + SIZEOF_PTR;
&mut member_array[start..end]
};
// Store offset to start of member, MUST BE ADJUSTED LATER BASED ON THE ADDRESS OF THE BUFFER
member_ptr.copy_from_slice(&cur_offset.to_ne_bytes());
vec.extend(member);
vec.push(0);
}
vec
};
let mut buffer = match destbuf {
None => MaybeAllocated::Owned(Box::into_pin(strings.into_boxed_slice())),
Some(buf) => {
let mut buf = MaybeAllocated::Borrowed(buf);
if buf.len() < strings.len() {
platform::ERRNO.set(errno::ERANGE);
return Err(Error::BufTooSmall);
}
buf[..strings.len()].copy_from_slice(&strings);
buf
}
};
let reference = split(&mut buffer).ok_or(Error::Other)?;
Ok(OwnedGrp { buffer, reference })
}
/// MT-Unsafe race:grgid locale
///
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getgrgid.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getgrgid(gid: gid_t) -> *mut group {
let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else {
return ptr::null_mut();
};
for line in BufReader::new(db).lines() {
let Ok(line) = line else {
return ptr::null_mut();
};
let Ok(grp) = parse_grp(line, None) else {
return ptr::null_mut();
};
if grp.reference.gr_gid == gid {
return grp.into_global();
}
}
ptr::null_mut()
}
/// MT-Unsafe race:grnam locale
///
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getgrnam.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getgrnam(name: *const c_char) -> *mut group {
let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else {
return ptr::null_mut();
};
for line in BufReader::new(db).lines() {
let Ok(line) = line else {
return ptr::null_mut();
};
let Ok(grp) = parse_grp(line, None) else {
return ptr::null_mut();
};
// Attempt to prevent BO vulnerabilities
if unsafe {
strncmp(
grp.reference.gr_name,
name,
strlen(grp.reference.gr_name).min(strlen(name)),
) == 0
} {
return grp.into_global();
}
}
ptr::null_mut()
}
/// MT-Safe locale
///
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getgrgid_r.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getgrgid_r(
gid: gid_t,
grp: *mut group,
result_buf: *mut group,
buffer: *mut c_char,
bufsize: usize,
buflen: usize,
result: *mut *mut group,
) -> c_int {
unimplemented!();
// In case of error or the requested entry is not found.
unsafe {
*result = ptr::null_mut();
}
let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else {
return ENOENT;
};
for line in BufReader::new(db).lines() {
let Ok(line) = line else { return EINVAL };
let grp = match parse_grp(
line,
Some(DestBuffer {
ptr: buffer.cast::<u8>(),
len: buflen,
}),
) {
Ok(grp) => grp,
Err(err) => {
return match err {
Error::BufTooSmall => ERANGE,
Error::EOF
| Error::SyntaxError
| Error::FromUtf8Error(_)
| Error::ParseIntError(_)
| Error::Other => EINVAL,
Error::Misc(io_err) => match io_err.kind() {
io::ErrorKind::InvalidData | io::ErrorKind::UnexpectedEof => EINVAL,
io::ErrorKind::NotFound => ENOENT,
_ => EIO,
},
};
}
};
if grp.reference.gr_gid == gid {
unsafe {
*result_buf = grp.reference;
*result = result_buf;
}
return 0;
}
}
// The requested entry was not found.
0
}
// #[no_mangle]
pub extern "C" fn getgrnam_r(
/// MT-Safe locale
///
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getgrnam_r.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getgrnam_r(
name: *const c_char,
grp: *mut group,
result_buf: *mut group,
buffer: *mut c_char,
bufsize: usize,
buflen: usize,
result: *mut *mut group,
) -> c_int {
unimplemented!();
let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else {
return ENOENT;
};
for line in BufReader::new(db).lines() {
let Ok(line) = line else { return EINVAL };
let Ok(grp) = parse_grp(
line,
Some(DestBuffer {
ptr: buffer.cast::<u8>(),
len: buflen,
}),
) else {
return EINVAL;
};
if unsafe {
strncmp(
grp.reference.gr_name,
name,
strlen(grp.reference.gr_name).min(strlen(name)),
) == 0
} {
unsafe {
*result_buf = grp.reference;
*result = result_buf;
}
return 0;
}
}
ENOENT
}
// #[no_mangle]
pub extern "C" fn getgrent() -> *mut group {
unimplemented!();
/// MT-Unsafe race:grent race:grentbuf locale
///
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endgrent.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getgrent() -> *mut group {
let mut line_reader = unsafe { &mut *LINE_READER.get() };
if line_reader.is_none() {
let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else {
return ptr::null_mut();
};
*line_reader = Some(BufReader::new(db).lines());
}
if let Some(lines) = line_reader.deref_mut() {
let Some(line) = lines.next() else {
return ptr::null_mut();
};
let Ok(line) = line else {
return ptr::null_mut();
};
if let Ok(grp) = parse_grp(line, None) {
grp.into_global()
} else {
ptr::null_mut()
}
} else {
ptr::null_mut()
}
}
// #[no_mangle]
pub extern "C" fn endgrent() {
unimplemented!();
/// MT-Unsafe race:grent locale
///
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endgrent.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn endgrent() {
unsafe {
*(&mut *LINE_READER.get()) = None;
}
}
// #[no_mangle]
pub extern "C" fn setgrent() {
unimplemented!();
/// MT-Unsafe race:grent locale
///
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endgrent.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn setgrent() {
let line_reader = unsafe { &mut *LINE_READER.get() };
let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else {
return;
};
*line_reader = Some(BufReader::new(db).lines());
}
/*
#[no_mangle]
pub extern "C" fn func(args) -> c_int {
unimplemented!();
/// MT-Safe locale
/// Not POSIX
///
/// See <https://www.man7.org/linux/man-pages/man3/getgrouplist.3.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getgrouplist(
user: *const c_char,
group: gid_t,
groups: *mut gid_t,
ngroups: *mut c_int,
) -> c_int {
let grps = unsafe {
slice::from_raw_parts_mut(groups.cast::<MaybeUninit<gid_t>>(), ngroups.read() as usize)
};
// FIXME: This API probably expects the group database to already exist in memory, as it
// doesn't seem to have any documented error handling.
let Ok(user) = (unsafe { crate::c_str::CStr::from_ptr(user).to_str() }) else {
return 0;
};
let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else {
return 0;
};
let mut groups_found: c_int = 0;
for line in BufReader::new(db).lines() {
let Ok(line) = line else {
return 0;
};
let mut parts = line.split(SEPARATOR);
let group_name = parts.next().unwrap_or("");
let group_password = parts.next().unwrap_or("");
let group_id = parts.next().unwrap_or("-1").parse::<c_int>().unwrap();
let members = parts
.next()
.unwrap_or("")
.split(",")
.map(|i| i.trim())
.collect::<Vec<_>>();
if !members.contains(&user) {
continue;
}
if let Some(dst) = grps.get_mut(groups_found as usize) {
dst.write(group_id);
}
groups_found = match groups_found.checked_add(1) {
Some(g) => g,
None => break,
};
}
unsafe {
ngroups.write(groups_found);
}
if groups_found as usize > grps.len() {
-1
} else {
groups_found
}
}
/// MT-Safe locale
/// Not POSIX
///
/// See <https://www.man7.org/linux/man-pages/man3/initgroups.3.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn initgroups(user: *const c_char, gid: gid_t) -> c_int {
let mut groups = [0; limits::NGROUPS_MAX];
let mut count = groups.len() as c_int;
if unsafe { getgrouplist(user, gid, groups.as_mut_ptr(), &raw mut count) < 0 } {
return -1;
}
unsafe { setgroups(count as size_t, groups.as_ptr()) }
}
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man2/setgroups.2.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn setgroups(size: size_t, list: *const gid_t) -> c_int {
unsafe { Sys::setgroups(size, list) }
.map(|()| 0)
.or_minus_one_errno()
}
*/
+17
View File
@@ -0,0 +1,17 @@
# netinet/in.h brings in sys/socket.h
sys_includes = ["features.h", "netinet/in.h"]
include_guard = "_RELIBC_IFADDRS_H"
language = "C"
style = "Tag"
no_includes = true
cpp_compat = true
trailer = """
#define ifa_broadaddr ifa_ifu.ifu_broadaddr
#define ifa_dstaddr ifa_ifu.ifu_dstaddr
"""
[export.rename]
"sockaddr" = "struct sockaddr"
[enum]
prefix_with_name = true
+44
View File
@@ -0,0 +1,44 @@
//! `ifaddrs.h` implementation.
//!
//! Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getifaddrs.3.html>.
use crate::{
header::{errno, stdlib, sys_socket::sockaddr},
platform::{
self,
types::{c_char, c_int, c_uint, c_void},
},
};
#[repr(C)]
union ifaddrs_ifa_ifu {
ifu_broadaddr: *mut sockaddr,
ifu_dstaddr: *mut sockaddr,
}
#[repr(C)]
pub struct ifaddrs {
ifa_next: *mut ifaddrs,
ifa_name: *mut c_char,
ifa_flags: c_uint,
ifa_addr: *mut sockaddr,
ifa_netmask: *mut sockaddr,
ifa_ifu: ifaddrs_ifa_ifu,
ifa_data: *mut c_void,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn freeifaddrs(mut ifa: *mut ifaddrs) {
while !ifa.is_null() {
let next = unsafe { (*ifa).ifa_next };
unsafe { stdlib::free(ifa.cast()) };
ifa = next;
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getifaddrs(ifap: *mut *mut ifaddrs) -> c_int {
//TODO: implement getifaddrs
platform::ERRNO.set(errno::ENOSYS);
-1
}
+207 -2
View File
@@ -1,6 +1,211 @@
sys_includes = ["stdint.h", "wchar.h"]
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/inttypes.h.html
#
# Spec quotations relating to includes:
# - "The <inttypes.h> header shall include the <stdint.h> header."
# - "wchar_t As described in <stddef.h>."
#
# Include stdint.h and stddef.h directly per POSIX spec.
# Do NOT include wchar.h — it creates a circular dependency:
# wchar.h → stdint.h → gnulib inttypes.h → inttypes.h → wchar.h
sys_includes = ["stdint.h", "stddef.h"]
include_guard = "_RELIBC_INTTYPES_H"
trailer = "#include <bits/inttypes.h>"
trailer = """
#ifndef _RELIBC_BITS_INTTYPES_H
#define _RELIBC_BITS_INTTYPES_H
#define PRId8 "hhd"
#define PRId16 "hd"
#define PRId32 "d"
#define PRId64 "ld"
#define PRIdLEAST8 "hhd"
#define PRIdLEAST16 "hd"
#define PRIdLEAST32 "d"
#define PRIdLEAST64 "ld"
#define PRIdFAST8 "hhd"
#define PRIdFAST16 "hd"
#define PRIdFAST32 "d"
#define PRIdFAST64 "ld"
#define PRIi8 "hhi"
#define PRIi16 "hi"
#define PRIi32 "i"
#define PRIi64 "li"
#define PRIiLEAST8 "hhi"
#define PRIiLEAST16 "hi"
#define PRIiLEAST32 "i"
#define PRIiLEAST64 "li"
#define PRIiFAST8 "hhi"
#define PRIiFAST16 "hi"
#define PRIiFAST32 "i"
#define PRIiFAST64 "li"
#define PRIo8 "hho"
#define PRIo16 "ho"
#define PRIo32 "o"
#define PRIo64 "lo"
#define PRIoLEAST8 "hho"
#define PRIoLEAST16 "ho"
#define PRIoLEAST32 "o"
#define PRIoLEAST64 "lo"
#define PRIoFAST8 "hho"
#define PRIoFAST16 "ho"
#define PRIoFAST32 "o"
#define PRIoFAST64 "lo"
#define PRIu8 "hhu"
#define PRIu16 "hu"
#define PRIu32 "u"
#define PRIu64 "lu"
#define PRIuLEAST8 "hhu"
#define PRIuLEAST16 "hu"
#define PRIuLEAST32 "u"
#define PRIuLEAST64 "lu"
#define PRIuFAST8 "hhu"
#define PRIuFAST16 "hu"
#define PRIuFAST32 "u"
#define PRIuFAST64 "lu"
#define PRIx8 "hhx"
#define PRIx16 "hx"
#define PRIx32 "x"
#define PRIx64 "lx"
#define PRIxLEAST8 "hhx"
#define PRIxLEAST16 "hx"
#define PRIxLEAST32 "x"
#define PRIxLEAST64 "lx"
#define PRIxFAST8 "hhx"
#define PRIxFAST16 "hx"
#define PRIxFAST32 "x"
#define PRIxFAST64 "lx"
#define PRIX8 "hhX"
#define PRIX16 "hX"
#define PRIX32 "X"
#define PRIX64 "lX"
#define PRIXLEAST8 "hhX"
#define PRIXLEAST16 "hX"
#define PRIXLEAST32 "X"
#define PRIXLEAST64 "lX"
#define PRIXFAST8 "hhX"
#define PRIXFAST16 "hX"
#define PRIXFAST32 "X"
#define PRIXFAST64 "lX"
#define PRIdMAX "jd"
#define PRIiMAX "ji"
#define PRIoMAX "jo"
#define PRIuMAX "ju"
#define PRIxMAX "jx"
#define PRIXMAX "jX"
#define PRIdPTR "td"
#define PRIiPTR "ti"
#define PRIoPTR "to"
#define PRIuPTR "tu"
#define PRIxPTR "tx"
#define PRIXPTR "tX"
#define SCNd8 PRId8
#define SCNd16 PRId16
#define SCNd32 PRId32
#define SCNd64 PRId64
#define SCNdLEAST8 PRIdLEAST8
#define SCNdLEAST16 PRIdLEAST16
#define SCNdLEAST32 PRIdLEAST32
#define SCNdLEAST64 PRIdLEAST64
#define SCNdFAST8 PRIdFAST8
#define SCNdFAST16 PRIdFAST16
#define SCNdFAST32 PRIdFAST32
#define SCNdFAST64 PRIdFAST64
#define SCNi8 PRIi8
#define SCNi16 PRIi16
#define SCNi32 PRIi32
#define SCNi64 PRIi64
#define SCNiLEAST8 PRIiLEAST8
#define SCNiLEAST16 PRIiLEAST16
#define SCNiLEAST32 PRIiLEAST32
#define SCNiLEAST64 PRIiLEAST64
#define SCNiFAST8 PRIiFAST8
#define SCNiFAST16 PRIiFAST16
#define SCNiFAST32 PRIiFAST32
#define SCNiFAST64 PRIiFAST64
#define SCNo8 PRIo8
#define SCNo16 PRIo16
#define SCNo32 PRIo32
#define SCNo64 PRIo64
#define SCNoLEAST8 PRIoLEAST8
#define SCNoLEAST16 PRIoLEAST16
#define SCNoLEAST32 PRIoLEAST32
#define SCNoLEAST64 PRIoLEAST64
#define SCNoFAST8 PRIoFAST8
#define SCNoFAST16 PRIoFAST16
#define SCNoFAST32 PRIoFAST32
#define SCNoFAST64 PRIoFAST64
#define SCNu8 PRIu8
#define SCNu16 PRIu16
#define SCNu32 PRIu32
#define SCNu64 PRIu64
#define SCNuLEAST8 PRIuLEAST8
#define SCNuLEAST16 PRIuLEAST16
#define SCNuLEAST32 PRIuLEAST32
#define SCNuLEAST64 PRIuLEAST64
#define SCNuFAST8 PRIuFAST8
#define SCNuFAST16 PRIuFAST16
#define SCNuFAST32 PRIuFAST32
#define SCNuFAST64 PRIuFAST64
#define SCNx8 PRIx8
#define SCNx16 PRIx16
#define SCNx32 PRIx32
#define SCNx64 PRIx64
#define SCNxLEAST8 PRIxLEAST8
#define SCNxLEAST16 PRIxLEAST16
#define SCNxLEAST32 PRIxLEAST32
#define SCNxLEAST64 PRIxLEAST64
#define SCNxFAST8 PRIxFAST8
#define SCNxFAST16 PRIxFAST16
#define SCNxFAST32 PRIxFAST32
#define SCNxFAST64 PRIxFAST64
#define SCNdMAX PRIdMAX
#define SCNiMAX PRIiMAX
#define SCNoMAX PRIoMAX
#define SCNuMAX PRIuMAX
#define SCNxMAX PRIxMAX
#define SCNdPTR PRIdPTR
#define SCNiPTR PRIiPTR
#define SCNoPTR PRIoPTR
#define SCNuPTR PRIuPTR
#define SCNxPTR PRIxPTR
#endif // _RELIBC_BITS_INTTYPES_H
"""
language = "C"
style = "Type"
no_includes = true
+48 -23
View File
@@ -1,21 +1,34 @@
//! `inttypes.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/inttypes.h.html>.
use crate::{
header::{ctype, errno::*, stdlib::*},
platform::{self, types::*},
header::{
ctype::{self, isspace},
errno::{EINVAL, ERANGE},
stdlib::{convert_hex, convert_integer, convert_octal, detect_base, is_positive},
},
platform::{
self,
types::{c_char, c_int, c_long, intmax_t, uintmax_t, wchar_t},
},
};
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/imaxabs.html>.
#[unsafe(no_mangle)]
pub extern "C" fn imaxabs(i: intmax_t) -> intmax_t {
i.abs()
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/inttypes.h.html>.
#[repr(C)]
pub struct imaxdiv_t {
quot: intmax_t,
rem: intmax_t,
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/imaxdiv.html>.
#[unsafe(no_mangle)]
pub extern "C" fn imaxdiv(i: intmax_t, j: intmax_t) -> imaxdiv_t {
imaxdiv_t {
quot: i / j,
@@ -23,7 +36,8 @@ pub extern "C" fn imaxdiv(i: intmax_t, j: intmax_t) -> imaxdiv_t {
}
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strtoimax.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn strtoimax(
s: *const c_char,
endptr: *mut *mut c_char,
@@ -32,15 +46,16 @@ pub unsafe extern "C" fn strtoimax(
strto_impl!(
intmax_t,
false,
intmax_t::max_value(),
intmax_t::min_value(),
intmax_t::MAX,
intmax_t::MIN,
s,
endptr,
base
)
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strtoimax.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn strtoumax(
s: *const c_char,
endptr: *mut *mut c_char,
@@ -49,30 +64,40 @@ pub unsafe extern "C" fn strtoumax(
strto_impl!(
uintmax_t,
false,
uintmax_t::max_value(),
uintmax_t::min_value(),
uintmax_t::MAX,
uintmax_t::MIN,
s,
endptr,
base
)
}
#[allow(unused)]
// #[no_mangle]
pub extern "C" fn wcstoimax(
nptr: *const wchar_t,
endptr: *mut *mut wchar_t,
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcstoimax.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wcstoimax(
mut ptr: *const wchar_t,
end: *mut *mut wchar_t,
base: c_int,
) -> intmax_t {
unimplemented!();
skipws!(ptr);
let result = strto_impl!(intmax_t, ptr, base);
if !end.is_null() {
unsafe { *end = ptr.cast_mut() };
}
result
}
#[allow(unused)]
// #[no_mangle]
pub extern "C" fn wcstoumax(
nptr: *const wchar_t,
endptr: *mut *mut wchar_t,
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcstoimax.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wcstoumax(
mut ptr: *const wchar_t,
end: *mut *mut wchar_t,
base: c_int,
) -> uintmax_t {
unimplemented!();
skipws!(ptr);
let result = strtou_impl!(uintmax_t, ptr, base);
if !end.is_null() {
unsafe { *end = ptr.cast_mut() };
}
result
}
+10
View File
@@ -0,0 +1,10 @@
sys_includes = ["stddef.h", "stdint.h", "features.h"]
include_guard = "_RELIBC_LANGINFO_H"
after_includes = "#include <bits/locale-t.h> // locale_t"
language = "C"
style = "tag"
no_includes = false
cpp_compat = true
[enum]
prefix_with_name = true
+225
View File
@@ -0,0 +1,225 @@
//! `langinfo.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/langinfo.h.html>.
// TODO : involve loading locale data. Currently, the implementation only supports the "C" locale.
use crate::header::bits_locale_t::locale_t;
use core::ffi::c_char;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/langinfo.h.html>.
///
/// POSIX type for items used with `nl_langinfo`
/// In practice, this is an integer index into the string table.
pub type nl_item = i32;
// Static string table for langinfo constants
static STRING_TABLE: [&[u8]; 81] = [
b"UTF-8\0", // CODESET
b"%a %b %e %H:%M:%S %Y\0", // D_T_FMT
b"%m/%d/%y\0", // D_FMT
b"%H:%M:%S\0", // T_FMT
b"%I:%M:%S %p\0", // T_FMT_AMPM
b"AM\0", // AM_STR
b"PM\0", // PM_STR
b"Sunday\0", // DAY_1
b"Monday\0", // DAY_2
b"Tuesday\0", // DAY_3
b"Wednesday\0", // DAY_4
b"Thursday\0", // DAY_5
b"Friday\0", // DAY_6
b"Saturday\0", // DAY_7
b"Sun\0", // ABDAY_1
b"Mon\0", // ABDAY_2
b"Tue\0", // ABDAY_3
b"Wed\0", // ABDAY_4
b"Thu\0", // ABDAY_5
b"Fri\0", // ABDAY_6
b"Sat\0", // ABDAY_7
b"January\0", // MON_1
b"February\0", // MON_2
b"March\0", // MON_3
b"April\0", // MON_4
b"May\0", // MON_5
b"June\0", // MON_6
b"July\0", // MON_7
b"August\0", // MON_8
b"September\0", // MON_9
b"October\0", // MON_10
b"November\0", // MON_11
b"December\0", // MON_12
b"Jan\0", // ABMON_1
b"Feb\0", // ABMON_2
b"Mar\0", // ABMON_3
b"Apr\0", // ABMON_4
b"May\0", // ABMON_5
b"Jun\0", // ABMON_6
b"Jul\0", // ABMON_7
b"Aug\0", // ABMON_8
b"Sep\0", // ABMON_9
b"Oct\0", // ABMON_10
b"Nov\0", // ABMON_11
b"Dec\0", // ABMON_12
b"\0", // ERA
b"\0", // ERA_D_FMT
b"\0", // ERA_D_T_FMT
b"\0", // ERA_T_FMT
b"\0", // ALT_DIGITS
b".\0", // RADIXCHAR
b"\0", // THOUSEP
b"^[yY]\0", // YESEXPR
b"^[nN]\0", // NOEXPR
b"yes\0", // YESSTR
b"no\0", // NOSTR
b".\0", // CRNCYSTR
// Some languages have alternative names for
// months. For the "C" locale, we just use one.
b"January\0", // ALTMON_1
b"February\0", // ALTMON_2
b"March\0", // ALTMON_3
b"April\0", // ALTMON_4
b"May\0", // ALTMON_5
b"June\0", // ALTMON_6
b"July\0", // ALTMON_7
b"August\0", // ALTMON_8
b"September\0", // ALTMON_9
b"October\0", // ALTMON_10
b"November\0", // ALTMON_11
b"December\0", // ALTMON_12
b"Jan\0", // ABALTMON_1
b"Feb\0", // ABALTMON_2
b"Mar\0", // ABALTMON_3
b"Apr\0", // ABALTMON_4
b"May\0", // ABALTMON_5
b"Jun\0", // ABALTMON_6
b"Jul\0", // ABALTMON_7
b"Aug\0", // ABALTMON_8
b"Sep\0", // ABALTMON_9
b"Oct\0", // ABALTMON_10
b"Nov\0", // ABALTMON_11
b"Dec\0", // ABALTMON_12
];
// Item constants
pub const CODESET: nl_item = 0;
pub const D_T_FMT: nl_item = 1;
pub const D_FMT: nl_item = 2;
pub const T_FMT: nl_item = 3;
pub const T_FMT_AMPM: nl_item = 4;
pub const AM_STR: nl_item = 5;
pub const PM_STR: nl_item = 6;
pub const DAY_1: nl_item = 7;
pub const DAY_2: nl_item = 8;
pub const DAY_3: nl_item = 9;
pub const DAY_4: nl_item = 10;
pub const DAY_5: nl_item = 11;
pub const DAY_6: nl_item = 12;
pub const DAY_7: nl_item = 13;
pub const ABDAY_1: nl_item = 14;
pub const ABDAY_2: nl_item = 15;
pub const ABDAY_3: nl_item = 16;
pub const ABDAY_4: nl_item = 17;
pub const ABDAY_5: nl_item = 18;
pub const ABDAY_6: nl_item = 19;
pub const ABDAY_7: nl_item = 20;
pub const MON_1: nl_item = 21;
pub const MON_2: nl_item = 22;
pub const MON_3: nl_item = 23;
pub const MON_4: nl_item = 24;
pub const MON_5: nl_item = 25;
pub const MON_6: nl_item = 26;
pub const MON_7: nl_item = 27;
pub const MON_8: nl_item = 28;
pub const MON_9: nl_item = 29;
pub const MON_10: nl_item = 30;
pub const MON_11: nl_item = 31;
pub const MON_12: nl_item = 32;
pub const ABMON_1: nl_item = 33;
pub const ABMON_2: nl_item = 34;
pub const ABMON_3: nl_item = 35;
pub const ABMON_4: nl_item = 36;
pub const ABMON_5: nl_item = 37;
pub const ABMON_6: nl_item = 38;
pub const ABMON_7: nl_item = 39;
pub const ABMON_8: nl_item = 40;
pub const ABMON_9: nl_item = 41;
pub const ABMON_10: nl_item = 42;
pub const ABMON_11: nl_item = 43;
pub const ABMON_12: nl_item = 44;
pub const ERA: nl_item = 45;
pub const ERA_D_FMT: nl_item = 46;
pub const ERA_D_T_FMT: nl_item = 47;
pub const ERA_T_FMT: nl_item = 48;
pub const ALT_DIGITS: nl_item = 49;
pub const RADIXCHAR: nl_item = 50;
pub const THOUSEP: nl_item = 51;
pub const YESEXPR: nl_item = 52;
pub const NOEXPR: nl_item = 53;
pub const YESSTR: nl_item = 54; // Legacy
pub const NOSTR: nl_item = 55; // Legacy
pub const CRNCYSTR: nl_item = 56;
pub const ALTMON_1: nl_item = 57;
pub const ALTMON_2: nl_item = 58;
pub const ALTMON_3: nl_item = 59;
pub const ALTMON_4: nl_item = 60;
pub const ALTMON_5: nl_item = 61;
pub const ALTMON_6: nl_item = 62;
pub const ALTMON_7: nl_item = 63;
pub const ALTMON_8: nl_item = 64;
pub const ALTMON_9: nl_item = 65;
pub const ALTMON_10: nl_item = 66;
pub const ALTMON_11: nl_item = 67;
pub const ALTMON_12: nl_item = 68;
pub const ABALTMON_1: nl_item = 69;
pub const ABALTMON_2: nl_item = 70;
pub const ABALTMON_3: nl_item = 71;
pub const ABALTMON_4: nl_item = 72;
pub const ABALTMON_5: nl_item = 73;
pub const ABALTMON_6: nl_item = 74;
pub const ABALTMON_7: nl_item = 75;
pub const ABALTMON_8: nl_item = 76;
pub const ABALTMON_9: nl_item = 77;
pub const ABALTMON_10: nl_item = 78;
pub const ABALTMON_11: nl_item = 79;
pub const ABALTMON_12: nl_item = 80;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/nl_langinfo.html>.
///
/// Get a string from the langinfo table
///
/// # Safety
/// - Caller must ensure `item` is a valid `nl_item` index.
/// - Returns a pointer to a null-terminated string, or an empty string if the item is invalid.
/// - Compatibility requires mutable pointer to be returned, but it should not be mutated!
#[unsafe(no_mangle)]
pub unsafe extern "C" fn nl_langinfo(item: nl_item) -> *mut c_char {
// Validate the item and perform the lookup
let ptr = if (item as usize) < STRING_TABLE.len() {
STRING_TABLE[item as usize].as_ptr().cast::<c_char>()
} else {
// Return a pointer to an empty string if the item is invalid
c"".as_ptr().cast::<c_char>()
};
// Mutable pointer is required (unsafe!)
ptr.cast_mut()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/nl_langinfo_l.html>.
///
/// Get a string from the langinfo table
///
/// # Safety
/// - Caller must ensure `item` is a valid `nl_item` index.
/// - Returns a pointer to a null-terminated string, or an empty string if the item is invalid.
/// - Compatibility requires mutable pointer to be returned, but it should not be mutated!
#[unsafe(no_mangle)]
pub unsafe extern "C" fn nl_langinfo_l(item: nl_item, _loc: locale_t) -> *mut c_char {
unsafe { nl_langinfo(item) }
}
+3
View File
@@ -1,3 +1,6 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/libgen.h.html
#
# There are no spec quotations relating to includes
sys_includes = []
include_guard = "_RELIBC_LIBGEN_H"
language = "C"
+27 -19
View File
@@ -1,47 +1,55 @@
//! libgen implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/libgen.h.html
//! `libgen.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/libgen.h.html>.
use crate::platform::types::c_char;
use crate::header::string::strlen;
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/basename.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn basename(str: *mut c_char) -> *mut c_char {
if str.is_null() || strlen(str) == 0 {
return ".\0".as_ptr() as *mut c_char;
if str.is_null() || unsafe { strlen(str) == 0 } {
return c".".as_ptr().cast_mut();
}
let mut end = strlen(str) as isize - 1;
while end >= 0 && *str.offset(end) == b'/' as c_char {
let mut end = unsafe { strlen(str) as isize - 1 };
while end >= 0 && unsafe { *str.offset(end) == b'/' as c_char } {
end -= 1;
}
if end == -1 {
return "/\0".as_ptr() as *mut c_char;
return c"/".as_ptr().cast_mut();
}
let mut begin = end;
while begin >= 0 && *str.offset(begin) != b'/' as c_char {
while begin >= 0 && unsafe { *str.offset(begin) != b'/' as c_char } {
begin -= 1;
}
*str.offset(end + 1) = 0;
str.offset(begin + 1) as *mut c_char
unsafe {
*str.offset(end + 1) = 0;
str.offset(begin + 1).cast::<c_char>()
}
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dirname.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dirname(str: *mut c_char) -> *mut c_char {
if str.is_null() || strlen(str) == 0 {
return ".\0".as_ptr() as *mut c_char;
if str.is_null() || unsafe { strlen(str) == 0 } {
return c".".as_ptr().cast_mut();
}
let mut end = strlen(str) as isize - 1;
while end > 0 && *str.offset(end) == b'/' as c_char {
let mut end = unsafe { strlen(str) as isize - 1 };
while end > 0 && unsafe { *str.offset(end) == b'/' as c_char } {
end -= 1;
}
while end >= 0 && *str.offset(end) != b'/' as c_char {
while end >= 0 && unsafe { *str.offset(end) != b'/' as c_char } {
end -= 1;
}
while end > 0 && *str.offset(end) == b'/' as c_char {
while end > 0 && unsafe { *str.offset(end) == b'/' as c_char } {
end -= 1;
}
if end == -1 {
return ".\0".as_ptr() as *mut c_char;
return c".".as_ptr().cast_mut();
}
unsafe {
*str.offset(end + 1) = 0;
}
*str.offset(end + 1) = 0;
str
}
+3 -1
View File
@@ -1,6 +1,8 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/limits.h.html
#
# There are no spec quotations relating to includes
sys_includes = []
include_guard = "_RELIBC_LIMITS_H"
trailer = "#include <bits/limits.h>"
language = "C"
style = "Tag"
no_includes = true
+129 -1
View File
@@ -1,3 +1,131 @@
//! limits.h implementation for relibc
//! `limits.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/limits.h.html>.
use crate::platform::types::{
c_char, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, c_ulong, c_ulonglong,
c_ushort, ssize_t,
};
pub const NAME_MAX: usize = 255;
pub const PASS_MAX: usize = 128;
pub const PATH_MAX: usize = 4096;
pub const NGROUPS_MAX: usize = 65536;
pub const CHAR_BIT: u32 = 8;
pub const WORD_BIT: u32 = 32;
#[cfg(target_pointer_width = "32")]
pub const LONG_BIT: u32 = 32;
#[cfg(target_pointer_width = "64")]
pub const LONG_BIT: u32 = 64;
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
pub const CHAR_MAX: c_char = 0xFF;
#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
pub const CHAR_MAX: c_char = 0x7F;
pub const SCHAR_MAX: c_schar = 0x7F;
pub const SHRT_MAX: c_short = 0x7FFF;
pub const INT_MAX: c_int = 0x7FFF_FFFF;
#[cfg(target_pointer_width = "32")]
pub const LONG_MAX: c_long = 0x7FFF_FFFF;
#[cfg(target_pointer_width = "64")]
pub const LONG_MAX: c_long = 0x7FFF_FFFF_FFFF_FFFF;
pub const LLONG_MAX: c_longlong = 0x7FFF_FFFF_FFFF_FFFF;
#[cfg(target_pointer_width = "32")]
pub const SSIZE_MAX: ssize_t = 0x7FFF_FFFF;
#[cfg(target_pointer_width = "64")]
pub const SSIZE_MAX: ssize_t = 0x7FFF_FFFF_FFFF_FFFF;
pub const UCHAR_MAX: c_uchar = 0xFF;
pub const USHRT_MAX: c_ushort = 0xFFFF;
pub const UINT_MAX: c_uint = 0xFFFFFFFF;
#[cfg(target_pointer_width = "32")]
pub const ULONG_MAX: c_ulong = 0xFFFF_FFFF;
#[cfg(target_pointer_width = "64")]
pub const ULONG_MAX: c_ulong = 0xFFFF_FFFF_FFFF_FFFF;
pub const ULLONG_MAX: c_ulonglong = 0xFFFF_FFFF_FFFF_FFFF;
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
pub const CHAR_MIN: c_char = 0;
#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
pub const CHAR_MIN: c_char = -0x80;
pub const SCHAR_MIN: c_schar = -SCHAR_MAX - 1;
pub const SHRT_MIN: c_short = -SHRT_MAX - 1;
pub const INT_MIN: c_int = -INT_MAX - 1;
pub const LONG_MIN: c_long = -LONG_MAX - 1;
pub const LLONG_MIN: c_longlong = -LLONG_MAX - 1;
// TODO: 4096 for most architectures as determined by a quick grep of musl's source; need a better
// way to determine it for other archs or to hard code a value.
#[cfg(target_os = "linux")]
pub const PAGE_SIZE: usize = 4096;
// These POSIX symbols must have these values regardless of OS
pub const _POSIX_AIO_LISTIO_MAX: c_long = 2;
pub const _POSIX_AIO_MAX: c_long = 1;
pub const _POSIX_ARG_MAX: c_long = 4096;
pub const _POSIX_CHILD_MAX: c_long = 25;
pub const _POSIX_CLOCKRES_MIN: c_long = 20000000;
pub const _POSIX_DELAYTIMER_MAX: c_long = 32;
pub const _POSIX_HOST_NAME_MAX: c_long = 255;
pub const _POSIX_LINK_MAX: c_long = 8;
pub const _POSIX_LOGIN_NAME_MAX: c_long = 9;
pub const _POSIX_MAX_CANON: c_long = 255;
pub const _POSIX_MAX_INPUT: c_long = 255;
pub const _POSIX_NAME_MAX: c_long = 14;
pub const _POSIX_NGROUPS_MAX: c_long = 8;
pub const _POSIX_OPEN_MAX: c_long = 20;
pub const _POSIX_PATH_MAX: c_long = 256;
pub const _POSIX_PIPE_BUF: c_long = 512;
pub const _POSIX_RE_DUP_MAX: c_long = 255;
pub const _POSIX_RTSIG_MAX: c_long = 8;
pub const _POSIX_SEM_NSEMS_MAX: c_long = 256;
pub const _POSIX_SEM_VALUE_MAX: c_long = 32767;
pub const _POSIX_SIGQUEUE_MAX: c_long = 32;
pub const _POSIX_SSIZE_MAX: c_long = 32767;
pub const _POSIX_STREAM_MAX: c_long = 8;
pub const _POSIX_SYMLINK_MAX: c_long = 255;
pub const _POSIX_SYMLOOP_MAX: c_long = 8;
pub const _POSIX_THREAD_DESTRUCTOR_ITERATIONS: c_long = 4;
pub const _POSIX_THREAD_KEYS_MAX: c_long = 128;
pub const _POSIX_THREAD_THREADS_MAX: c_long = 64;
pub const _POSIX_TIMER_MAX: c_long = 32;
pub const _POSIX_TTY_NAME_MAX: c_long = 9;
pub const _POSIX_TZNAME_MAX: c_long = 6;
pub const _POSIX2_BC_BASE_MAX: c_long = 99;
pub const _POSIX2_BC_DIM_MAX: c_long = 2048;
pub const _POSIX2_BC_SCALE_MAX: c_long = 99;
pub const _POSIX2_BC_STRING_MAX: c_long = 1000;
pub const _POSIX2_CHARCLASS_NAME_MAX: c_long = 14;
pub const _POSIX2_COLL_WEIGHTS_MAX: c_long = 2;
pub const _POSIX2_EXPR_NEST_MAX: c_long = 32;
pub const _POSIX2_LINE_MAX: c_long = 2048;
pub const _POSIX2_RE_DUP_MAX: c_long = 255;
// These symbols must be at least the POSIX values, and sysconf will return the actual value between
// the posix minimum and this maximum.
pub const BC_BASE_MAX: c_long = _POSIX2_BC_BASE_MAX;
pub const BC_DIM_MAX: c_long = _POSIX2_BC_DIM_MAX;
pub const BC_SCALE_MAX: c_long = _POSIX2_BC_SCALE_MAX;
pub const BC_STRING_MAX: c_long = _POSIX2_BC_STRING_MAX;
pub const CHARCLASS_NAME_MAX: c_long = _POSIX2_CHARCLASS_NAME_MAX;
pub const COLL_WEIGHTS_MAX: c_long = _POSIX2_COLL_WEIGHTS_MAX;
pub const EXPR_NEST_MAX: c_long = _POSIX2_EXPR_NEST_MAX;
pub const LINE_MAX: c_long = _POSIX2_LINE_MAX;
pub const RE_DUP_MAX: c_long = _POSIX2_RE_DUP_MAX;
pub const HOST_NAME_MAX: c_long = _POSIX_HOST_NAME_MAX;
pub const LOGIN_NAME_MAX: c_long = 255;
pub const GETENTROPY_MAX: c_long = 256;
pub const LINK_MAX: c_long = 127;
pub const PIPE_BUF: c_long = 4096;
pub const FILESIZEBITS: c_long = 64;
pub const MAX_CANON: c_long = _POSIX_MAX_CANON;
pub const MAX_INPUT: c_long = _POSIX_MAX_INPUT;
pub const SYMLINK_MAX: c_long = _POSIX_SYMLINK_MAX;
pub const POSIX_ALLOC_SIZE_MIN: c_long = 4096;
pub const PTHREAD_DESTRUCTOR_ITERATIONS: c_long = _POSIX_THREAD_DESTRUCTOR_ITERATIONS;
// TODO: What should this limit be? Both glibc and musl have it as 1024
pub const PTHREAD_KEYS_MAX: c_long = 4096 * 32;
pub const PTHREAD_STACK_MIN: c_long = 65536;
+9 -2
View File
@@ -1,8 +1,15 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/locale.h.html
#
# Spec quotations relating to includes:
# - "The <locale.h> header shall define NULL (as described in <stddef.h>) and at least the following as macros:"
sys_includes = ["stddef.h"]
include_guard = "_RELIBC_LOCALE_H"
trailer = "#include <bits/locale.h>"
after_includes = """
#include <bits/locale-t.h> // for locale_t
"""
language = "C"
style = "Tag"
no_includes = true
no_includes = false
cpp_compat = true
[enum]
+16
View File
@@ -0,0 +1,16 @@
use crate::platform::types::c_int;
pub const LC_COLLATE: c_int = 0;
pub const LC_CTYPE: c_int = 1;
pub const LC_MESSAGES: c_int = 2;
pub const LC_MONETARY: c_int = 3;
pub const LC_NUMERIC: c_int = 4;
pub const LC_TIME: c_int = 5;
pub const LC_ALL: c_int = 6;
pub const LC_COLLATE_MASK: c_int = 0x1;
pub const LC_CTYPE_MASK: c_int = 0x2;
pub const LC_MESSAGES_MASK: c_int = 0x4;
pub const LC_MONETARY_MASK: c_int = 0x8;
pub const LC_NUMERIC_MASK: c_int = 0x10;
pub const LC_TIME_MASK: c_int = 0x20;
pub const LC_ALL_MASK: c_int = 0b111111;
+392
View File
@@ -0,0 +1,392 @@
use core::str::FromStr;
use alloc::{boxed::Box, ffi::CString, string::String, vec::Vec};
use super::constants::*;
use crate::platform::types::{c_char, c_int};
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/locale.h.html>.
/// this struct is not ordered like in the posix spec for readability
#[repr(C)]
#[derive(Clone)]
pub struct lconv {
pub decimal_point: *mut c_char,
pub thousands_sep: *mut c_char,
pub grouping: *mut c_char,
pub int_curr_symbol: *mut c_char,
pub currency_symbol: *mut c_char,
pub mon_decimal_point: *mut c_char,
pub mon_thousands_sep: *mut c_char,
pub mon_grouping: *mut c_char,
pub positive_sign: *mut c_char,
pub negative_sign: *mut c_char,
pub int_frac_digits: c_char,
pub frac_digits: c_char,
pub p_cs_precedes: c_char,
pub p_sep_by_space: c_char,
pub n_cs_precedes: c_char,
pub n_sep_by_space: c_char,
pub p_sign_posn: c_char,
pub n_sign_posn: c_char,
pub int_p_cs_precedes: c_char,
pub int_p_sep_by_space: c_char,
pub int_n_cs_precedes: c_char,
pub int_n_sep_by_space: c_char,
pub int_p_sign_posn: c_char,
pub int_n_sign_posn: c_char,
}
unsafe impl Sync for lconv {}
/// "POSIX" or "C" default
pub(crate) const fn posix_lconv() -> lconv {
lconv {
// numeric, non-monetary
decimal_point: c".".as_ptr().cast_mut(),
thousands_sep: c"".as_ptr().cast_mut(),
grouping: c"".as_ptr().cast_mut(),
// local monetary
int_curr_symbol: c"".as_ptr().cast_mut(),
currency_symbol: c"".as_ptr().cast_mut(),
mon_decimal_point: c"".as_ptr().cast_mut(),
mon_thousands_sep: c"".as_ptr().cast_mut(),
mon_grouping: c"".as_ptr().cast_mut(),
positive_sign: c"".as_ptr().cast_mut(),
negative_sign: c"".as_ptr().cast_mut(),
// delimiters, unspecified
int_frac_digits: c_char::MAX,
frac_digits: c_char::MAX,
p_cs_precedes: c_char::MAX,
p_sep_by_space: c_char::MAX,
n_cs_precedes: c_char::MAX,
n_sep_by_space: c_char::MAX,
p_sign_posn: c_char::MAX,
n_sign_posn: c_char::MAX,
// international format
int_p_cs_precedes: c_char::MAX,
int_p_sep_by_space: c_char::MAX,
int_n_cs_precedes: c_char::MAX,
int_n_sep_by_space: c_char::MAX,
int_p_sign_posn: c_char::MAX,
int_n_sign_posn: c_char::MAX,
}
}
#[repr(C)]
pub(crate) struct LocaleData {
pub name: CString,
pub lconv: lconv,
// Owned memory buffers
pub decimal_point: CString,
pub thousands_sep: CString,
pub grouping: Vec<c_char>,
pub int_curr_symbol: CString,
pub currency_symbol: CString,
pub mon_decimal_point: CString,
pub mon_thousands_sep: CString,
pub mon_grouping: Vec<c_char>,
pub positive_sign: CString,
pub negative_sign: CString,
}
unsafe impl Sync for LocaleData {}
impl LocaleData {
pub fn new(name: CString, defs: PosixLocaleDef) -> Box<Self> {
let mut data = Box::new(LocaleData {
name,
decimal_point: Self::to_cstring(defs.decimal_point),
thousands_sep: Self::to_cstring(defs.thousands_sep),
grouping: Self::to_grouping_char(defs.grouping),
int_curr_symbol: Self::to_cstring(defs.int_curr_symbol),
currency_symbol: Self::to_cstring(defs.currency_symbol),
mon_decimal_point: Self::to_cstring(defs.mon_decimal_point),
mon_thousands_sep: Self::to_cstring(defs.mon_thousands_sep),
mon_grouping: Self::to_grouping_char(defs.mon_grouping),
positive_sign: Self::to_cstring(defs.positive_sign),
negative_sign: Self::to_cstring(defs.negative_sign),
lconv: unsafe { core::mem::zeroed() },
});
data.lconv.int_frac_digits = defs.int_frac_digits.unwrap_or(c_char::MAX);
data.lconv.frac_digits = defs.frac_digits.unwrap_or(c_char::MAX);
data.lconv.p_cs_precedes = defs.p_cs_precedes.unwrap_or(c_char::MAX);
data.lconv.p_sep_by_space = defs.p_sep_by_space.unwrap_or(c_char::MAX);
data.lconv.n_cs_precedes = defs.n_cs_precedes.unwrap_or(c_char::MAX);
data.lconv.n_sep_by_space = defs.n_sep_by_space.unwrap_or(c_char::MAX);
data.lconv.p_sign_posn = defs.p_sign_posn.unwrap_or(c_char::MAX);
data.lconv.n_sign_posn = defs.n_sign_posn.unwrap_or(c_char::MAX);
data.lconv.int_p_cs_precedes = defs.int_p_cs_precedes.unwrap_or(c_char::MAX);
data.lconv.int_p_sep_by_space = defs.int_p_sep_by_space.unwrap_or(c_char::MAX);
data.lconv.int_n_cs_precedes = defs.int_n_cs_precedes.unwrap_or(c_char::MAX);
data.lconv.int_n_sep_by_space = defs.int_n_sep_by_space.unwrap_or(c_char::MAX);
data.lconv.int_p_sign_posn = defs.int_p_sign_posn.unwrap_or(c_char::MAX);
data.lconv.int_n_sign_posn = defs.int_n_sign_posn.unwrap_or(c_char::MAX);
data.update_lconv_pointers();
data
}
pub fn posix() -> Box<Self> {
LocaleData::new(CString::from_str("C").unwrap(), PosixLocaleDef::default())
}
fn update_lconv_pointers(&mut self) {
self.lconv.decimal_point = self.decimal_point.as_ptr().cast_mut();
self.lconv.thousands_sep = self.thousands_sep.as_ptr().cast_mut();
self.lconv.grouping = self.grouping.as_ptr().cast_mut();
self.lconv.int_curr_symbol = self.int_curr_symbol.as_ptr().cast_mut();
self.lconv.currency_symbol = self.currency_symbol.as_ptr().cast_mut();
self.lconv.mon_decimal_point = self.mon_decimal_point.as_ptr().cast_mut();
self.lconv.mon_thousands_sep = self.mon_thousands_sep.as_ptr().cast_mut();
self.lconv.mon_grouping = self.mon_grouping.as_ptr().cast_mut();
self.lconv.positive_sign = self.positive_sign.as_ptr().cast_mut();
self.lconv.negative_sign = self.negative_sign.as_ptr().cast_mut();
}
pub fn copy_category(&mut self, other: &Self, category: c_int) {
match category {
LC_NUMERIC => {
self.decimal_point = other.decimal_point.clone();
self.thousands_sep = other.thousands_sep.clone();
self.grouping = other.grouping.clone();
self.lconv.frac_digits = other.lconv.frac_digits;
}
LC_MONETARY => {
self.int_curr_symbol = other.int_curr_symbol.clone();
self.currency_symbol = other.currency_symbol.clone();
self.mon_decimal_point = other.mon_decimal_point.clone();
self.mon_thousands_sep = other.mon_thousands_sep.clone();
self.mon_grouping = other.mon_grouping.clone();
self.positive_sign = other.positive_sign.clone();
self.negative_sign = other.negative_sign.clone();
self.lconv.int_frac_digits = other.lconv.int_frac_digits;
self.lconv.p_cs_precedes = other.lconv.p_cs_precedes;
self.lconv.p_sep_by_space = other.lconv.p_sep_by_space;
self.lconv.n_cs_precedes = other.lconv.n_cs_precedes;
self.lconv.n_sep_by_space = other.lconv.n_sep_by_space;
self.lconv.p_sign_posn = other.lconv.p_sign_posn;
self.lconv.n_sign_posn = other.lconv.n_sign_posn;
self.lconv.int_p_cs_precedes = other.lconv.int_p_cs_precedes;
self.lconv.int_p_sep_by_space = other.lconv.int_p_sep_by_space;
self.lconv.int_n_cs_precedes = other.lconv.int_n_cs_precedes;
self.lconv.int_n_sep_by_space = other.lconv.int_n_sep_by_space;
self.lconv.int_p_sign_posn = other.lconv.int_p_sign_posn;
self.lconv.int_n_sign_posn = other.lconv.int_n_sign_posn;
}
LC_ALL => {
*self = other.clone();
}
_ => {}
}
self.update_lconv_pointers();
}
fn to_cstring(opt: Option<CString>) -> CString {
opt.unwrap_or_else(|| CString::new("").unwrap())
}
fn to_grouping_char(opt: Vec<Option<c_char>>) -> Vec<c_char> {
let mut v: Vec<c_char> = opt.into_iter().map(Self::to_char).collect();
v.push(0);
v
}
fn to_char(opt: Option<c_char>) -> c_char {
opt.unwrap_or(c_char::MAX)
}
}
impl Clone for LocaleData {
fn clone(&self) -> Self {
let mut data = Self {
name: self.name.clone(),
lconv: self.lconv.clone(),
decimal_point: self.decimal_point.clone(),
thousands_sep: self.thousands_sep.clone(),
grouping: self.grouping.clone(),
int_curr_symbol: self.int_curr_symbol.clone(),
currency_symbol: self.currency_symbol.clone(),
mon_decimal_point: self.mon_decimal_point.clone(),
mon_thousands_sep: self.mon_thousands_sep.clone(),
mon_grouping: self.mon_grouping.clone(),
positive_sign: self.positive_sign.clone(),
negative_sign: self.negative_sign.clone(),
};
data.update_lconv_pointers();
data
}
}
#[derive(Clone)]
pub(crate) struct GlobalLocaleData {
// names per LC_* constant
pub names: [CString; 7],
pub data: LocaleData,
}
impl GlobalLocaleData {
pub fn new() -> Box<Self> {
let data = LocaleData::posix();
let names = [
data.name.clone(),
data.name.clone(),
data.name.clone(),
data.name.clone(),
data.name.clone(),
data.name.clone(),
data.name.clone(),
];
let mut r = Box::new(GlobalLocaleData { data: *data, names });
r.data.update_lconv_pointers();
r
}
pub fn get_name(&self, category: i32) -> Option<&CString> {
self.names.get(category as usize)
}
pub fn set_name(&mut self, category: i32, name: CString) -> Option<&CString> {
if self.names.get(category as usize).is_some() {
self.names[category as usize] = name;
self.names.get(category as usize)
} else {
None
}
}
}
unsafe impl Sync for GlobalLocaleData {}
#[derive(Default)]
pub(crate) struct PosixLocaleDef {
pub decimal_point: Option<CString>,
pub thousands_sep: Option<CString>,
pub grouping: Vec<Option<c_char>>,
pub int_curr_symbol: Option<CString>,
pub currency_symbol: Option<CString>,
pub mon_decimal_point: Option<CString>,
pub mon_thousands_sep: Option<CString>,
pub mon_grouping: Vec<Option<c_char>>,
pub positive_sign: Option<CString>,
pub negative_sign: Option<CString>,
pub int_frac_digits: Option<c_char>,
pub frac_digits: Option<c_char>,
pub p_cs_precedes: Option<c_char>,
pub p_sep_by_space: Option<c_char>,
pub n_cs_precedes: Option<c_char>,
pub n_sep_by_space: Option<c_char>,
pub p_sign_posn: Option<c_char>,
pub n_sign_posn: Option<c_char>,
pub int_p_cs_precedes: Option<c_char>,
pub int_p_sep_by_space: Option<c_char>,
pub int_n_cs_precedes: Option<c_char>,
pub int_n_sep_by_space: Option<c_char>,
pub int_p_sign_posn: Option<c_char>,
pub int_n_sign_posn: Option<c_char>,
}
impl PosixLocaleDef {
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap07.html>
pub fn parse(content: &str) -> Self {
let mut locale = PosixLocaleDef::default();
let mut lines = content.lines();
loop {
let Some(line) = lines.next() else {
break;
};
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with('#') {
continue;
}
let mut parts = trimmed.split_ascii_whitespace();
let Some(key) = parts.next() else {
continue;
};
let mut val: String = String::new();
loop {
let Some(chunk) = parts.next() else {
break;
};
if !chunk.ends_with('\\') {
val.push_str(chunk);
break;
}
// multiline values
val.push_str(&chunk[0..chunk.len() - 1]);
let Some(next_line) = lines.next() else {
break;
};
parts = next_line.split_ascii_whitespace();
}
if key.is_empty() || val.is_empty() {
continue;
}
match key {
"decimal_point" => locale.decimal_point = Self::parse_str(&val),
"thousands_sep" => locale.thousands_sep = Self::parse_str(&val),
"int_curr_symbol" => locale.int_curr_symbol = Self::parse_str(&val),
"currency_symbol" => locale.currency_symbol = Self::parse_str(&val),
"mon_decimal_point" => locale.mon_decimal_point = Self::parse_str(&val),
"mon_thousands_sep" => locale.mon_thousands_sep = Self::parse_str(&val),
"positive_sign" => locale.positive_sign = Self::parse_str(&val),
"negative_sign" => locale.negative_sign = Self::parse_str(&val),
"grouping" => locale.grouping = Self::parse_int_group(&val),
"mon_grouping" => locale.mon_grouping = Self::parse_int_group(&val),
"int_frac_digits" => locale.int_frac_digits = Self::parse_int(&val),
"frac_digits" => locale.frac_digits = Self::parse_int(&val),
"p_cs_precedes" => locale.p_cs_precedes = Self::parse_int(&val),
"p_sep_by_space" => locale.p_sep_by_space = Self::parse_int(&val),
"n_cs_precedes" => locale.n_cs_precedes = Self::parse_int(&val),
"n_sep_by_space" => locale.n_sep_by_space = Self::parse_int(&val),
"p_sign_posn" => locale.p_sign_posn = Self::parse_int(&val),
"n_sign_posn" => locale.n_sign_posn = Self::parse_int(&val),
"int_p_cs_precedes" => locale.int_p_cs_precedes = Self::parse_int(&val),
"int_p_sep_by_space" => locale.int_p_sep_by_space = Self::parse_int(&val),
"int_n_cs_precedes" => locale.int_n_cs_precedes = Self::parse_int(&val),
"int_n_sep_by_space" => locale.int_n_sep_by_space = Self::parse_int(&val),
"int_p_sign_posn" => locale.int_p_sign_posn = Self::parse_int(&val),
"int_n_sign_posn" => locale.int_n_sign_posn = Self::parse_int(&val),
_ => {}
}
}
locale
}
/// parse e.g. `3;3;0` -> [ 3,3,0 ], `-1` -> [ None ]
fn parse_int_group(val: &str) -> Vec<Option<c_char>> {
val.split(';').map(Self::parse_int).collect()
}
/// parse e.g. `-1` -> None, `1` -> Some(1)
fn parse_int(val: &str) -> Option<c_char> {
let r = val.trim().parse::<c_char>().ok();
// c_char is u8 on aarch64 and riscv64 so comparison is useless
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
if r.is_some_and(|i| i < 0) {
return None;
}
r
}
/// parse e.g. `""`
fn parse_str(val: &str) -> Option<CString> {
let mut r = String::new();
let mut v = val.chars();
if v.next() != Some('"') {
return None;
}
while let Some(c) = v.next() {
if c == '"' {
if v.next().is_some() {
return None;
}
break;
}
// TODO: Parse <..>
r.push(c);
}
CString::new(r).ok()
}
}
+172 -58
View File
@@ -1,68 +1,182 @@
//! locale implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/locale.h.html
//! `locale.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/locale.h.html>.
use core::ptr;
use alloc::{boxed::Box, ffi::CString, string::String};
use core::{ptr, str::FromStr};
use crate::platform::types::*;
use crate::{
c_str::CStr,
error::{Errno, ResultExtPtrMut},
fs::File,
header::{errno, fcntl},
io::Read,
platform::types::{c_char, c_int},
};
const EMPTY_PTR: *const c_char = "\0" as *const _ as *const c_char;
// Can't use &str because of the mutability
static mut C_LOCALE: [c_char; 2] = [b'C' as c_char, 0];
#[repr(C)]
#[no_mangle]
pub struct lconv {
currency_symbol: *const c_char,
decimal_point: *const c_char,
frac_digits: c_char,
grouping: *const c_char,
int_curr_symbol: *const c_char,
int_frac_digits: c_char,
mon_decimal_point: *const c_char,
mon_grouping: *const c_char,
mon_thousands_sep: *const c_char,
negative_sign: *const c_char,
n_cs_precedes: c_char,
n_sep_by_space: c_char,
n_sign_posn: c_char,
positive_sign: *const c_char,
p_cs_precedes: c_char,
p_sep_by_space: c_char,
p_sign_posn: c_char,
thousands_sep: *const c_char,
}
unsafe impl Sync for lconv {}
mod constants;
use constants::*;
mod data;
use data::*;
static CURRENT_LOCALE: lconv = lconv {
currency_symbol: EMPTY_PTR,
decimal_point: ".\0" as *const _ as *const c_char,
frac_digits: c_char::max_value(),
grouping: EMPTY_PTR,
int_curr_symbol: EMPTY_PTR,
int_frac_digits: c_char::max_value(),
mon_decimal_point: EMPTY_PTR,
mon_grouping: EMPTY_PTR,
mon_thousands_sep: EMPTY_PTR,
negative_sign: EMPTY_PTR,
n_cs_precedes: c_char::max_value(),
n_sep_by_space: c_char::max_value(),
n_sign_posn: c_char::max_value(),
positive_sign: EMPTY_PTR,
p_cs_precedes: c_char::max_value(),
p_sep_by_space: c_char::max_value(),
p_sign_posn: c_char::max_value(),
thousands_sep: EMPTY_PTR,
};
use super::bits_locale_t::locale_t;
/// constant struct to "C" or "POSIX" locale
/// mutable because POSIX demands a mutable pointer
static mut POSIX_LOCALE: lconv = posix_lconv();
pub const LC_GLOBAL_LOCALE: locale_t = -1isize as locale_t;
/// process-wide locale, used by setlocale() and localeconv()
static mut GLOBAL_LOCALE: *mut GlobalLocaleData = ptr::null_mut();
/// thread-wide locale, used by uselocale() and localeconv()
#[thread_local]
pub(crate) static mut THREAD_LOCALE: *mut LocaleData = ptr::null_mut();
#[no_mangle]
pub extern "C" fn localeconv() -> *const lconv {
&CURRENT_LOCALE as *const _
}
#[no_mangle]
pub unsafe extern "C" fn setlocale(_option: c_int, val: *const c_char) -> *mut c_char {
if val.is_null() {
return C_LOCALE.as_mut_ptr() as *mut c_char;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/localeconv.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn localeconv() -> *mut lconv {
let current = unsafe { uselocale(ptr::null_mut()) };
if current == LC_GLOBAL_LOCALE || current.is_null() {
if !unsafe { GLOBAL_LOCALE.is_null() } {
// safety: GLOBAL_LOCALE is never set to null again
unsafe { &raw mut (*GLOBAL_LOCALE).data.lconv }
} else {
&raw mut POSIX_LOCALE
}
} else {
let current = current.cast::<LocaleData>();
unsafe { &raw mut (*current).lconv }
}
// TODO actually implement
ptr::null_mut()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setlocale.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn setlocale(category: c_int, locale: *const c_char) -> *mut c_char {
if unsafe { GLOBAL_LOCALE.is_null() } {
let new_global = GlobalLocaleData::new();
unsafe { GLOBAL_LOCALE = Box::into_raw(new_global) };
};
let Some(global) = (unsafe { GLOBAL_LOCALE.as_mut() }) else {
return ptr::null_mut();
};
if locale.is_null() {
let Some(name) = global.get_name(category) else {
return ptr::null_mut();
};
return name.as_ptr().cast_mut();
}
let name = unsafe { CStr::from_ptr(locale).to_str().unwrap_or("C") };
let locale_file = if name.is_empty() || name == "C" || name == "POSIX" {
// TODO: name == "" should read from LANG env
Ok(LocaleData::posix())
} else {
load_locale_file(name)
};
match locale_file {
Ok(loc_ptr) => {
global.data.copy_category(&loc_ptr, category);
let Some(name) = global.set_name(category, CString::from_str(name).unwrap()) else {
return ptr::null_mut();
};
name.as_ptr().cast_mut()
}
Err(_) => ptr::null_mut(),
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/uselocale.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn uselocale(newloc: locale_t) -> locale_t {
let old_loc = if unsafe { THREAD_LOCALE.is_null() } {
LC_GLOBAL_LOCALE
} else {
(unsafe { THREAD_LOCALE }) as locale_t
};
if !newloc.is_null() {
unsafe {
THREAD_LOCALE = if newloc == LC_GLOBAL_LOCALE {
ptr::null_mut()
} else {
newloc.cast::<LocaleData>()
}
};
}
old_loc
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/newlocale.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn newlocale(mask: c_int, locale: *const c_char, base: locale_t) -> locale_t {
let name = unsafe { CStr::from_ptr(locale) }
.to_string_lossy()
.into_owned();
let name = name.as_str();
let mut new_locale = if name.is_empty() || name == "C" || name == "POSIX" {
// TODO: name == "" should read from LANG env
Ok(LocaleData::posix())
} else {
load_locale_file(name)
};
if base != LC_GLOBAL_LOCALE {
// borrowing here
let base = base.cast_const().cast::<LocaleData>();
if let Ok(new_locale) = new_locale.as_mut()
&& let Some(base) = unsafe { base.as_ref() }
{
// copy old values if not containing the mask
if (mask & LC_NUMERIC_MASK) == 0 {
new_locale.copy_category(base, LC_NUMERIC);
}
if (mask & LC_MONETARY_MASK) == 0 {
new_locale.copy_category(base, LC_MONETARY);
}
// TODO: other categories?
}
}
new_locale.or_errno_null_mut().cast()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/freelocale.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn freelocale(loc: locale_t) {
if !loc.is_null() && loc != LC_GLOBAL_LOCALE {
drop(unsafe { Box::from_raw(loc.cast::<LocaleData>()) });
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/duplocale.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn duplocale(loc: locale_t) -> locale_t {
if loc.is_null() {
// TODO: errno?
loc
} else if loc == LC_GLOBAL_LOCALE {
Box::into_raw(LocaleData::posix()) as locale_t
} else {
// borrowing here
let loc = loc.cast_const().cast::<LocaleData>();
Box::into_raw(unsafe { Box::from((*loc).clone()) }) as locale_t
}
}
pub(crate) fn load_locale_file(name: &str) -> Result<Box<LocaleData>, Errno> {
let mut path = String::from("/usr/share/i18n/locales/");
path.push_str(name);
let path_c = CString::new(path).map_err(|_| Errno(errno::EINVAL))?;
let mut content = String::new();
let mut file = File::open(path_c.as_c_str().into(), fcntl::O_RDONLY)?;
file.read_to_string(&mut content)
.map_err(|_| Errno(errno::EIO))?;
let toml = PosixLocaleDef::parse(&content);
Ok(LocaleData::new(CString::from_str(name).unwrap(), toml))
}
+30
View File
@@ -0,0 +1,30 @@
sys_includes = ["features.h", "stddef.h"]
include_guard = "_RELIBC_MALLOC_H"
trailer = """
#ifndef _RELIBC_MALLOC_EXTRA_H
#define _RELIBC_MALLOC_EXTRA_H
#ifdef __cplusplus
extern "C" {
#endif
void *calloc(size_t nelem, size_t elsize);
void free(void *ptr);
void *malloc(size_t size);
void *memalign(size_t alignment, size_t size);
void *realloc(void *ptr, size_t size);
void *valloc(size_t size);
#ifdef __cplusplus
}
#endif
#endif
"""
language = "C"
style = "Type"
no_includes = true
cpp_compat = true
[enum]
prefix_with_name = true
+48
View File
@@ -0,0 +1,48 @@
//! `malloc.h` implementation.
//!
//! Non-POSIX, see <https://man7.org/linux/man-pages/man3/posix_memalign.3.html>.
use crate::{
header::errno::ENOMEM,
platform::{
self, Pal, Sys,
types::{c_void, size_t},
},
};
use core::ptr;
/// See <https://man7.org/linux/man-pages/man3/posix_memalign.3.html>.
#[deprecated]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pvalloc(size: size_t) -> *mut c_void {
let page_size = Sys::getpagesize();
// Find the smallest multiple of the page size in which the requested size
// will fit. The result of the division will always be less than or equal
// to size_t::MAX - 1, and the num_pages calculation will therefore never
// overflow.
let num_pages = if size != 0 {
(size - 1) / page_size + 1
} else {
0
};
match num_pages.checked_mul(page_size) {
Some(alloc_size) => {
let ptr = unsafe { platform::alloc_align(alloc_size, page_size) };
if ptr.is_null() {
platform::ERRNO.set(ENOMEM);
}
ptr
}
None => {
platform::ERRNO.set(ENOMEM);
ptr::null_mut()
}
}
}
/// See <https://man7.org/linux/man-pages/man3/malloc_usable_size.3.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn malloc_usable_size(ptr: *mut c_void) -> size_t {
unsafe { platform::alloc_usable_size(ptr) }
}
+57
View File
@@ -0,0 +1,57 @@
# Note: changing this file requires `make clean`.
sys_includes = []
include_guard = "_RELIBC_MATH_H"
language = "C"
style = "Type"
no_includes = true
cpp_compat = true
# constants from openlibm
after_includes = """
#define HUGE_VALF (float)HUGE_VAL
#define HUGE_VALL (long double)HUGE_VAL
#define INFINITY HUGE_VALF
#define NAN (__nan.__uf)
#define FP_ILOGB0 (-INT_MAX)
#define FP_ILOGBNAN INT_MAX
#define MAXFLOAT 3.40282346638528859812e+38F
#define M_E 2.7182818284590452354 /* e */
#define M_LOG2E 1.4426950408889634074 /* log_2 e */
#define M_LOG10E 0.43429448190325182765 /* log_10 e */
#define M_LN2 0.69314718055994530942 /* log_e 2 */
#define M_LN10 2.30258509299404568402 /* log_e 10 */
#define M_PI 3.14159265358979323846 /* pi */
#define M_PI_2 1.57079632679489661923 /* pi/2 */
#define M_PI_4 0.78539816339744830962 /* pi/4 */
#define M_1_PI 0.31830988618379067154 /* 1/pi */
#define M_2_PI 0.63661977236758134308 /* 2/pi */
#define M_2_SQRTPI 1.12837916709551257390 /* 2/sqrt(pi) */
#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */
#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */
#define FP_NAN 0
#define FP_INFINITE 1
#define FP_ZERO 2
#define FP_SUBNORMAL 3
#define FP_NORMAL 4
#define fpclassify(x) __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, x)
#define signbit(x) __builtin_signbit(x)
#define isnan(x) __builtin_isnan(x)
#define isinf(x) __builtin_isinf_sign(x)
#define isfinite(x) __builtin_isfinite(x)
#define isnormal(x) __builtin_isnormal(x)
#define isgreater(x, y) __builtin_isgreater((x), (y))
#define isgreaterequal(x, y) __builtin_isgreaterequal((x), (y))
#define isless(x, y) __builtin_isless((x), (y))
#define islessequal(x, y) __builtin_islessequal((x), (y))
#define islessgreater(x, y) __builtin_islessgreater((x), (y))
#define isunordered(x, y) __builtin_isunordered((x), (y))
"""
[enum]
prefix_with_name = true
+867
View File
@@ -0,0 +1,867 @@
//! `math.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/math.h.html>.
use crate::platform::types::{c_double, c_float, c_int, c_long, c_longlong};
// TODO move constants from cbindgen (openlibm)
// TODO all is* function is defined as "real-floating", so it can both f32 and f64
// TODO cover edge cases (EDOM and ERANGE)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/acos.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn acos(x: c_double) -> c_double {
libm::acos(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/acos.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn acosf(x: c_float) -> c_float {
libm::acosf(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/acosh.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn acosh(x: c_double) -> c_double {
libm::acosh(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/acosh.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn acoshf(x: c_float) -> c_float {
libm::acoshf(x)
}
// TODO acoshl, acosl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/asin.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn asin(x: c_double) -> c_double {
libm::asin(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/asin.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn asinf(x: c_float) -> c_float {
libm::asinf(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/asinh.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn asinh(x: c_double) -> c_double {
libm::asinh(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/asinh.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn asinhf(x: c_float) -> c_float {
libm::asinhf(x)
}
// TODO asinhl, asinl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/atan.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn atan(x: c_double) -> c_double {
libm::atan(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/atan2.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn atan2(y: c_double, x: c_double) -> c_double {
libm::atan2(y, x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/atan2.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn atan2f(y: c_float, x: c_float) -> c_float {
libm::atan2f(y, x)
}
// TODO atan2l (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/atan.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn atanf(x: c_float) -> c_float {
libm::atanf(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/atanh.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn atanh(x: c_double) -> c_double {
libm::atanh(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/atanh.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn atanhf(x: c_float) -> c_float {
libm::atanhf(x)
}
// TODO atanhl, atanl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/cbrt.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cbrt(x: c_double) -> c_double {
libm::cbrt(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/cbrt.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cbrtf(x: c_float) -> c_float {
libm::cbrtf(x)
}
// TODO cbrtl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ceil.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ceil(x: c_double) -> c_double {
libm::ceil(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ceil.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ceilf(x: c_float) -> c_float {
libm::ceilf(x)
}
// TODO ceill (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/copysign.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn copysign(x: c_double, y: c_double) -> c_double {
libm::copysign(x, y)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/copysign.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn copysignf(x: c_float, y: c_float) -> c_float {
libm::copysignf(x, y)
}
// TODO copysignl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/cos.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cos(x: c_double) -> c_double {
libm::cos(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/cos.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cosf(x: c_float) -> c_float {
libm::cosf(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/cosh.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cosh(x: c_double) -> c_double {
libm::cosh(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/cosh.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn coshf(x: c_float) -> c_float {
libm::coshf(x)
}
// TODO coshl, cosl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/erf.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn erf(x: c_double) -> c_double {
libm::erf(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/erfc.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn erfc(x: c_double) -> c_double {
libm::erfc(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/erfc.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn erfcf(x: c_float) -> c_float {
libm::erfcf(x)
}
// TODO erfcl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/erf.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn erff(x: c_float) -> c_float {
libm::erff(x)
}
// TODO erfl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/exp.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn exp(x: c_double) -> c_double {
libm::exp(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/exp2.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn exp2(x: c_double) -> c_double {
libm::exp2(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/exp2.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn exp2f(x: c_float) -> c_float {
libm::exp2f(x)
}
// TODO exp2l (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/exp.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn expf(x: c_float) -> c_float {
libm::expf(x)
}
// TODO expl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/expm1.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn expm1(x: c_double) -> c_double {
libm::expm1(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/expm1.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn expm1f(x: c_float) -> c_float {
libm::expm1f(x)
}
// TODO expm1l (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fabs.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn fabs(x: c_double) -> c_double {
libm::fabs(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fabsf.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn fabsf(x: c_float) -> c_float {
libm::fabsf(x)
}
// TODO fabsl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fdim.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn fdim(x: c_double, y: c_double) -> c_double {
libm::fdim(x, y)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fdim.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn fdimf(x: c_float, y: c_float) -> c_float {
libm::fdimf(x, y)
}
// TODO fdiml (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/floor.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn floor(x: c_double) -> c_double {
libm::floor(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/floor.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn floorf(x: c_float) -> c_float {
libm::floorf(x)
}
// TODO floorl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fma.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn fma(x: c_double, y: c_double, z: c_double) -> c_double {
libm::fma(x, y, z)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fma.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn fmaf(x: c_float, y: c_float, z: c_float) -> c_float {
libm::fmaf(x, y, z)
}
// TODO fmal (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fmax.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn fmax(x: c_double, y: c_double) -> c_double {
libm::fmax(x, y)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fmax.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn fmaxf(x: c_float, y: c_float) -> c_float {
libm::fmaxf(x, y)
}
// TODO fmaxl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fmin.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn fmin(x: c_double, y: c_double) -> c_double {
libm::fmin(x, y)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fmin.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn fminf(x: c_float, y: c_float) -> c_float {
libm::fminf(x, y)
}
// TODO fminl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fmod.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn fmod(x: c_double, y: c_double) -> c_double {
libm::fmod(x, y)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fmod.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn fmodf(x: c_float, y: c_float) -> c_float {
libm::fmodf(x, y)
}
// TODO fmodl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/frexp.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn frexp(num: c_double, exp: *mut c_int) -> c_double {
let (number, exponent) = libm::frexp(num);
unsafe {
*exp = exponent;
}
number
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/frexp.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn frexpf(num: c_float, exp: *mut c_int) -> c_float {
let (number, exponent) = libm::frexpf(num);
unsafe {
*exp = exponent;
}
number
}
// TODO frexpl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/hypot.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn hypot(x: c_double, y: c_double) -> c_double {
libm::hypot(x, y)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/hypot.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn hypotf(x: c_float, y: c_float) -> c_float {
libm::hypotf(x, y)
}
// TODO hypotl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ilogb.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ilogb(x: c_double) -> c_int {
libm::ilogb(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ilogb.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ilogbf(x: c_float) -> c_int {
libm::ilogbf(x)
}
// TODO ilogbl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isfinite.html>.
#[unsafe(no_mangle)]
unsafe extern "C" fn isfinite(x: c_double) -> c_int {
if x.is_finite() { 1 } else { 0 }
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isinf.html>.
#[unsafe(no_mangle)]
unsafe extern "C" fn isinf(x: c_double) -> c_int {
match x {
f64::INFINITY => 1,
f64::NEG_INFINITY => -1,
_ => 0,
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isnan.html>.
#[unsafe(no_mangle)]
unsafe extern "C" fn isnan(x: c_double) -> c_int {
if x.is_nan() { 1 } else { 0 }
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isnormal.html>.
#[unsafe(no_mangle)]
unsafe extern "C" fn isnormal(x: c_double) -> c_int {
if x.is_normal() { 1 } else { 0 }
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/j0.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn j0(x: c_double) -> c_double {
libm::j0(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/j0.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn j1(x: c_double) -> c_double {
libm::j1(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/j0.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jn(n: c_int, x: c_double) -> c_double {
libm::jn(n, x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ldexp.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ldexp(x: c_double, exp: c_int) -> c_double {
libm::ldexp(x, exp)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ldexp.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ldexpf(x: c_float, exp: c_int) -> c_float {
libm::ldexpf(x, exp)
}
// TODO ldexpl (long double)
#[unsafe(no_mangle)]
static mut signgam: c_int = 0;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/lgamma.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lgamma(x: c_double) -> c_double {
let (a, b) = libm::lgamma_r(x);
unsafe { signgam = b };
a
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/lgamma.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lgammaf(x: c_float) -> c_float {
let (a, b) = libm::lgammaf_r(x);
unsafe { signgam = b };
a
}
// TODO lgammal (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/llrint.html>.
#[unsafe(no_mangle)]
unsafe extern "C" fn llrint(x: c_double) -> c_longlong {
libm::rint(x) as _
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/llrintf.html>.
#[unsafe(no_mangle)]
unsafe extern "C" fn llrintf(x: c_float) -> c_longlong {
libm::rintf(x) as _
}
// TODO llrintl (long double to c_longlong)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/llround.html>.
#[unsafe(no_mangle)]
unsafe extern "C" fn llround(x: c_double) -> c_longlong {
libm::round(x) as _
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/llroundf.html>.
#[unsafe(no_mangle)]
unsafe extern "C" fn llroundf(x: c_float) -> c_longlong {
libm::roundf(x) as _
}
// TODO llroundl (long double to c_longlong)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/log.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn log(x: c_double) -> c_double {
libm::log(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/log10.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn log10(x: c_double) -> c_double {
libm::log10(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/log10.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn log10f(x: c_float) -> c_float {
libm::log10f(x)
}
// TODO log10l (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/log1p.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn log1p(x: c_double) -> c_double {
libm::log1p(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/log1p.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn log1pf(x: c_float) -> c_float {
libm::log1pf(x)
}
// TODO log1pl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/log2.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn log2(x: c_double) -> c_double {
libm::log2(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/log2.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn log2f(x: c_float) -> c_float {
libm::log2f(x)
}
// TODO log2l (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/logb.html>.
#[unsafe(no_mangle)]
unsafe extern "C" fn logb(x: c_double) -> c_double {
// TODO: errno
libm::ilogb(x) as _
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/logbf.html>.
#[unsafe(no_mangle)]
unsafe extern "C" fn logbf(x: c_float) -> c_float {
// TODO: errno
libm::ilogbf(x) as _
}
// TODO logbl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/log.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn logf(x: c_float) -> c_float {
libm::logf(x)
}
// TODO logl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/lrint.html>.
#[unsafe(no_mangle)]
unsafe extern "C" fn lrint(x: c_double) -> c_long {
// TODO: errno
libm::rint(x) as _
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/lrintf.html>.
#[unsafe(no_mangle)]
unsafe extern "C" fn lrintf(x: c_float) -> c_long {
// TODO: errno
libm::rintf(x) as _
}
// TODO lrintl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/lround.html>.
#[unsafe(no_mangle)]
unsafe extern "C" fn lround(x: c_double) -> c_long {
libm::round(x) as _
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/lroundf.html>.
#[unsafe(no_mangle)]
unsafe extern "C" fn lroundf(x: c_float) -> c_long {
libm::roundf(x) as _
}
// TODO lroundl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/modf.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn modf(x: c_double, iptr: *mut c_double) -> c_double {
let (integral, fractional) = libm::modf(x);
unsafe {
*iptr = integral;
}
fractional
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/modf.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn modff(value: c_float, iptr: *mut c_float) -> c_float {
let (integral, fractional) = libm::modff(value);
unsafe {
*iptr = integral;
}
fractional
}
// TODO modfl (long double)
// TODO do we want to support quiet NaN or just return 0?
// TODO nan (c_double)
// TODO nanf (c_float)
// TODO nanl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/nearbyint.html>.
#[unsafe(no_mangle)]
unsafe extern "C" fn nearbyint(x: c_double) -> c_double {
libm::rint(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/nearbyintf.html>.
#[unsafe(no_mangle)]
unsafe extern "C" fn nearbyintf(x: c_float) -> c_float {
libm::rintf(x)
}
// TODO nearbyintl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/nextafter.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn nextafter(x: c_double, y: c_double) -> c_double {
libm::nextafter(x, y)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/nextafter.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn nextafterf(x: c_float, y: c_float) -> c_float {
libm::nextafterf(x, y)
}
// TODO nextafterl (long double)
// TODO nexttoward (c_double)
// TODO nexttowardf (c_float)
// TODO nexttowardl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pow.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pow(x: c_double, y: c_double) -> c_double {
libm::pow(x, y)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pow.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn powf(x: c_float, y: c_float) -> c_float {
libm::powf(x, y)
}
// TODO powl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/remainder.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn remainder(x: c_double, y: c_double) -> c_double {
libm::remainder(x, y)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/remainder.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn remainderf(x: c_float, y: c_float) -> c_float {
libm::remainderf(x, y)
}
// TODO remainderl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/remquo.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn remquo(x: c_double, y: c_double, quo: *mut c_int) -> c_double {
let (remainder, quotient) = libm::remquo(x, y);
unsafe {
*quo = quotient;
}
remainder
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/remquo.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn remquof(x: c_float, y: c_float, quo: *mut c_int) -> c_float {
let (remainder, quotient) = libm::remquof(x, y);
unsafe {
*quo = quotient;
}
remainder
}
// TODO remquol (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/rint.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rint(x: c_double) -> c_double {
libm::rint(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/rint.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rintf(x: c_float) -> c_float {
libm::rintf(x)
}
// TODO rintl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/round.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn round(x: c_double) -> c_double {
libm::round(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/round.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn roundf(x: c_float) -> c_float {
libm::roundf(x)
}
// TODO roundl (long double)
// TODO scalbln (c_double, c_long)
// TODO scalblnf (c_float, c_long)
// TODO scalblnl (long double, c_long)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/scalbln.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn scalbn(x: c_double, n: c_int) -> c_double {
libm::scalbn(x, n)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/scalbln.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn scalbnf(x: c_float, n: c_int) -> c_float {
libm::scalbnf(x, n)
}
// TODO scalbnl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sin.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sin(x: c_double) -> c_double {
libm::sin(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sin.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sinf(x: c_float) -> c_float {
libm::sinf(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sinh.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sinh(x: c_double) -> c_double {
libm::sinh(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sinh.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sinhf(x: c_float) -> c_float {
libm::sinhf(x)
}
// TODO sinhl (long double)
// TODO sinl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sqrt.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sqrt(x: c_double) -> c_double {
libm::sqrt(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sqrt.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sqrtf(x: c_float) -> c_float {
libm::sqrtf(x)
}
// TODO sqrtl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tan.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tan(x: c_double) -> c_double {
libm::tan(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tan.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tanf(x: c_float) -> c_float {
libm::tanf(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tanh.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tanh(x: c_double) -> c_double {
libm::tanh(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tanh.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tanhf(x: c_float) -> c_float {
libm::tanhf(x)
}
// TODO tanhl (long double)
// TODO tanl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tgamma.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tgamma(x: c_double) -> c_double {
libm::tgamma(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tgamma.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tgammaf(x: c_float) -> c_float {
libm::tgammaf(x)
}
// TODO tgammal (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/trunc.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn trunc(x: c_double) -> c_double {
libm::trunc(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/trunc.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn truncf(x: c_float) -> c_float {
libm::truncf(x)
}
// TODO truncl (long double)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/y0.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn y0(x: c_double) -> c_double {
libm::y0(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/y0.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn y1(x: c_double) -> c_double {
libm::y1(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/y0.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn yn(n: c_int, x: c_double) -> c_double {
libm::yn(n, x)
}
+91 -1
View File
@@ -1,65 +1,155 @@
//! POSIX header implementations.
pub mod _aio;
pub mod _fenv;
pub mod arpa_inet;
pub mod assert;
pub mod bits_arpainet;
pub mod bits_eventfd;
pub mod bits_iovec;
#[path = "bits_locale-t/mod.rs"]
pub mod bits_locale_t;
pub mod bits_null;
pub mod bits_pthread;
#[path = "bits_safamily-t/mod.rs"]
pub mod bits_safamily_t;
#[path = "bits_sigset-t/mod.rs"]
pub mod bits_sigset_t;
#[path = "bits_size-t/mod.rs"]
pub mod bits_size_t;
#[path = "bits_socklen-t/mod.rs"]
pub mod bits_socklen_t;
pub mod bits_timespec;
#[path = "bits_wchar-t/mod.rs"]
pub mod bits_wchar_t;
// complex.h implemented in C
pub mod cpio;
pub mod crypt;
pub mod ctype;
// TODO: curses.h (deprecated)
// TODO: devctl.h
pub mod dirent;
#[path = "dl-tls/mod.rs"]
pub mod dl_tls;
pub mod dlfcn;
pub mod elf;
pub mod endian;
pub mod err;
pub mod errno;
pub mod fcntl;
pub mod float;
// TODO: fmtmsg.h
pub mod fnmatch;
// TODO: ftw.h
pub mod getopt;
pub mod glob;
pub mod grp;
// TODO: iconv.h
pub mod ifaddrs;
pub mod inttypes;
// iso646.h implemented in C
pub mod langinfo;
// TODO: libintl.h
pub mod libgen;
pub mod limits;
pub mod locale;
pub mod malloc;
// TODO unfinished, unguard feature when ready
#[cfg(feature = "math_libm")]
pub mod math;
pub mod monetary;
// TODO: mqueue.h
// TODO: ndbm.h
pub mod net_if;
pub mod netdb;
pub mod netinet_in;
pub mod netinet_ip;
pub mod netinet_tcp;
// TODO: nl_types.h
// TODO: Remove C header paths.h when cbindgen can export C/Rust strs
// pub mod paths;
pub mod poll;
pub mod pthread;
pub mod pty;
pub mod pwd;
// TODO: re_comp.h (deprecated)
pub mod regex;
// TODO: regexp.h (deprecated)
pub mod sched;
// TODO: search.h
pub mod semaphore;
pub mod setjmp;
pub mod sgtty;
pub mod shadow;
pub mod signal;
pub mod spawn;
// TODO: stdalign.h (likely C implementation)
// stdarg.h implemented in C
// stdatomic.h implemented in C
// stdbool.h implemented in C
pub mod stddef;
// stdint.h implemented in C
pub mod stdio;
pub mod stdlib;
// TODO: stdnoreturn.h (likely C implementation)
pub mod string;
pub mod strings;
// TODO: stropts.h (deprecated)
pub mod sys_auxv;
pub mod sys_epoll;
pub mod sys_eventfd;
pub mod sys_file;
pub mod sys_ioctl;
// TODO: sys/ipc.h
pub mod sys_mman;
// TODO: sys/msg.h
pub mod sys_ptrace;
pub mod sys_resource;
pub mod sys_select;
// TODO: sys/sem.h
// TODO: sys/shm.h
pub mod sys_socket;
pub mod sys_stat;
pub mod sys_statvfs;
#[allow(non_upper_case_globals)]
pub mod sys_syscall;
pub mod sys_time;
#[deprecated]
pub mod sys_timeb;
//pub mod sys_times;
pub mod _wctype;
pub mod arch_aarch64_user;
pub mod arch_riscv64_user;
pub mod arch_x64_user;
pub mod sys_signalfd;
#[cfg(not(target_arch = "x86"))] // TODO: x86
pub mod sys_procfs;
pub mod sys_random;
pub mod sys_timerfd;
pub mod sys_syslog;
pub mod sys_types;
#[allow(non_camel_case_types)]
pub mod sys_types_internal;
pub mod sys_uio;
pub mod sys_un;
pub mod sys_utsname;
pub mod sys_wait;
pub mod tar;
// TODO: term.h (deprecated)
pub mod termios;
// TODO: tgmath.h (likely C implementation)
// TODO: threads.h
pub mod time;
// TODO: uchar.h
// TODO: ucontext.h (deprecated)
// TODO: ulimit.h (deprecated)
// TODO: unctrl.h (deprecated)
pub mod unistd;
#[deprecated]
pub mod utime;
pub mod utmp;
// TODO: utmpx.h
// TODO: varargs.h (deprecated)
pub mod wchar;
pub mod wctype;
// TODO: wordexp.h
// TODO: xti.h (deprecated)
+150
View File
@@ -0,0 +1,150 @@
//! POSIX header implementations.
pub mod _aio;
pub mod _fenv;
pub mod arpa_inet;
pub mod assert;
pub mod bits_arpainet;
pub mod bits_eventfd;
pub mod bits_iovec;
#[path = "bits_locale-t/mod.rs"]
pub mod bits_locale_t;
pub mod bits_pthread;
#[path = "bits_safamily-t/mod.rs"]
pub mod bits_safamily_t;
#[path = "bits_sigset-t/mod.rs"]
pub mod bits_sigset_t;
#[path = "bits_socklen-t/mod.rs"]
pub mod bits_socklen_t;
pub mod bits_timespec;
// complex.h implemented in C
pub mod cpio;
pub mod crypt;
pub mod ctype;
// TODO: curses.h (deprecated)
// TODO: devctl.h
pub mod dirent;
#[path = "dl-tls/mod.rs"]
pub mod dl_tls;
pub mod dlfcn;
pub mod elf;
pub mod endian;
pub mod err;
pub mod errno;
pub mod fcntl;
pub mod float;
// TODO: fmtmsg.h
pub mod fnmatch;
// TODO: ftw.h
pub mod getopt;
pub mod glob;
pub mod grp;
// TODO: iconv.h
pub mod ifaddrs;
pub mod inttypes;
// iso646.h implemented in C
pub mod langinfo;
// TODO: libintl.h
pub mod libgen;
pub mod limits;
pub mod locale;
pub mod malloc;
// TODO unfinished, unguard feature when ready
#[cfg(feature = "math_libm")]
pub mod math;
pub mod monetary;
// TODO: mqueue.h
// TODO: ndbm.h
pub mod net_if;
pub mod netdb;
pub mod netinet_in;
pub mod netinet_ip;
pub mod netinet_tcp;
// TODO: nl_types.h
// TODO: Remove C header paths.h when cbindgen can export C/Rust strs
// pub mod paths;
pub mod poll;
pub mod pthread;
pub mod pty;
pub mod pwd;
// TODO: re_comp.h (deprecated)
pub mod regex;
// TODO: regexp.h (deprecated)
pub mod sched;
// TODO: search.h
pub mod semaphore;
pub mod setjmp;
pub mod sgtty;
pub mod shadow;
pub mod signal;
pub mod spawn;
// TODO: stdalign.h (likely C implementation)
// stdarg.h implemented in C
// stdatomic.h implemented in C
// stdbool.h implemented in C
// stddef.h implemented in C
// stdint.h implemented in C
pub mod stdio;
pub mod stdlib;
// TODO: stdnoreturn.h (likely C implementation)
pub mod string;
pub mod strings;
// TODO: stropts.h (deprecated)
pub mod sys_auxv;
pub mod sys_epoll;
pub mod sys_eventfd;
pub mod sys_file;
pub mod sys_ioctl;
// TODO: sys/ipc.h
pub mod sys_mman;
// TODO: sys/msg.h
pub mod sys_ptrace;
pub mod sys_resource;
pub mod sys_select;
// TODO: sys/sem.h
// TODO: sys/shm.h
pub mod sys_socket;
pub mod sys_stat;
pub mod sys_statvfs;
#[allow(non_upper_case_globals)]
pub mod sys_syscall;
pub mod sys_time;
#[deprecated]
pub mod sys_timeb;
//pub mod sys_times;
pub mod arch_aarch64_user;
pub mod arch_riscv64_user;
pub mod arch_x64_user;
pub mod sys_signalfd;
#[cfg(not(target_arch = "x86"))] // TODO: x86
pub mod sys_procfs;
pub mod sys_random;
pub mod sys_timerfd;
pub mod sys_syslog;
pub mod sys_types;
#[allow(non_camel_case_types)]
pub mod sys_types_internal;
pub mod sys_uio;
pub mod sys_un;
pub mod sys_utsname;
pub mod sys_wait;
pub mod tar;
// TODO: term.h (deprecated)
pub mod termios;
// TODO: tgmath.h (likely C implementation)
// TODO: threads.h
pub mod time;
// TODO: uchar.h
// TODO: ucontext.h (deprecated)
// TODO: ulimit.h (deprecated)
// TODO: unctrl.h (deprecated)
pub mod unistd;
#[deprecated]
pub mod utime;
pub mod utmp;
// TODO: utmpx.h
// TODO: varargs.h (deprecated)
pub mod wchar;
pub mod wctype;
// TODO: wordexp.h
// TODO: xti.h (deprecated)
+21
View File
@@ -0,0 +1,21 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/monetary.h.html
#
# Spec quotations relating to includes:
# - "The <monetary.h> header shall define the locale_t type as described in <locale.h>."
# - "The <monetary.h> header shall define the size_t type as described in <stddef.h>."
# - "The <monetary.h> header shall define the ssize_t type as described in <sys/types.h>."
#
# sys/types.h brings in stddef.h
sys_includes = ["sys/types.h"]
include_guard = "_RELIBC_MONETARY_H"
after_includes = """
#include <bits/locale-t.h> // for locale_t from locale.h
"""
language = "C"
style = "tag"
no_includes = true
cpp_compat = true
usize_is_size_t = true
[enum]
prefix_with_name = true
+102
View File
@@ -0,0 +1,102 @@
//! `monetary.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/monetary.h.html>.
// We should provide a strfmon() implementation that formats a monetary value,
// according to the current locale (TODO).
use alloc::string::{String, ToString};
use core::str;
use libm::{fabs, pow, round, trunc};
extern crate alloc;
mod strfmon;
#[repr(C)]
struct LocaleMonetaryInfo {
int_curr_symbol: &'static str,
currency_symbol: &'static str,
mon_decimal_point: &'static str,
mon_thousands_sep: &'static str,
mon_grouping: &'static [u8],
positive_sign: &'static str,
negative_sign: &'static str,
int_frac_digits: u8,
frac_digits: u8,
p_cs_precedes: bool,
p_sep_by_space: bool,
p_sign_posn: u8,
n_cs_precedes: bool,
n_sep_by_space: bool,
n_sign_posn: u8,
}
const DEFAULT_MONETARY: LocaleMonetaryInfo = LocaleMonetaryInfo {
int_curr_symbol: "USD ",
currency_symbol: "$",
mon_decimal_point: ".",
mon_thousands_sep: ",",
mon_grouping: &[3, 3],
positive_sign: "",
negative_sign: "-",
int_frac_digits: 2,
frac_digits: 2,
p_cs_precedes: true,
p_sep_by_space: false,
p_sign_posn: 1,
n_cs_precedes: true,
n_sep_by_space: false,
n_sign_posn: 1,
};
#[derive(Default)]
struct FormatFlags {
left_justify: bool,
force_sign: bool,
space_sign: bool,
use_parens: bool,
suppress_symbol: bool,
field_width: Option<usize>,
left_precision: Option<usize>,
right_precision: Option<usize>,
international: bool,
}
/// Formats a monetary value according to the current locale.
fn apply_grouping(int_str: &str, monetary: &LocaleMonetaryInfo) -> String {
let mut grouped = String::with_capacity(int_str.len() * 2);
let mut group_idx = 0;
let current_grouping = &monetary.mon_grouping;
let separator = monetary.mon_thousands_sep;
for (count, c) in int_str.chars().enumerate() {
if count > 0 {
let current_group = current_grouping[group_idx.min(current_grouping.len() - 1)];
if current_group > 0 && (count as u8).is_multiple_of(current_group) {
grouped.push_str(separator);
if group_idx + 1 < current_grouping.len() {
group_idx += 1;
}
}
}
grouped.push(c);
}
grouped
}
/// Safe handling of large monetary values. Returns None if the value is too large to format
fn format_value_parts(value: f64, frac_digits: usize) -> Option<(String, i64)> {
let abs_value = fabs(value);
if abs_value > (i64::MAX as f64) {
// Check if the value is too large to format
return None;
}
let int_part = trunc(abs_value) as i64;
let scale = pow(10.0, frac_digits as f64);
let frac_part = round((abs_value - int_part as f64) * scale) as i64;
Some((int_part.to_string(), frac_part))
}
+326
View File
@@ -0,0 +1,326 @@
use crate::platform::types::{c_char, size_t, ssize_t};
use super::{DEFAULT_MONETARY, FormatFlags, LocaleMonetaryInfo, apply_grouping};
use alloc::string::{String, ToString};
use core::{ffi::CStr, slice};
use libm::{fabs, pow, round, trunc};
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strfmon.html>.
///
/// The `strfmon()` function formats a monetary value according to the format string `format`
/// and writes the result to the character array `s` of size `maxsize`.
/// The format string can contain plain characters and format specifiers.
///
/// Returns:
/// - The number of characters written (excluding the null terminator), or -1 if
/// an error occurs (e.g., invalid input, buffer overflow)
#[unsafe(no_mangle)]
pub unsafe extern "C" fn strfmon(
s: *mut c_char, // Output buffer
maxsize: size_t, // Maximum size of the buffer
format: *const c_char, // Format string
mut args: ... // Variadic arguments for monetary values
) -> ssize_t {
// Validate input pointers and buffer size
if s.is_null() || format.is_null() || maxsize == 0 {
return -1; // Invalid input
}
// Convert the format string from C to string
let format_str = match unsafe { CStr::from_ptr(format) }.to_str() {
Ok(s) => s,
Err(_) => return -1, // Invalid format string
};
// Create a mutable slice for the output buffer
let buffer = unsafe { slice::from_raw_parts_mut(s.cast::<u8>(), maxsize) };
let mut pos = 0;
let mut format_chars = format_str.chars().peekable();
// Parse the format string
while let Some(c) = format_chars.next() {
// Handle plain characters (non-`%`)
if c != '%' {
if pos >= buffer.len() {
return -1; // Buffer overflow
}
buffer[pos] = c as u8; // Write the character to the buffer
pos += 1;
continue;
}
// Handle `%%` escape sequence
if format_chars.peek() == Some(&'%') {
if pos >= buffer.len() {
return -1; // Buffer overflow
}
buffer[pos] = b'%'; // Write a literal `%`
pos += 1;
format_chars.next(); // Skip the second `%`
continue;
}
// Parse format specifiers
let mut flags = FormatFlags::default();
// Parse flags (`+`, `(`, ...)
while let Some(&next_char) = format_chars.peek() {
match next_char {
'=' => flags.left_justify = true,
'+' => flags.force_sign = true,
' ' => flags.space_sign = true,
'(' => flags.use_parens = true,
'!' => flags.suppress_symbol = true,
_ => break, // Stop when no more flags are found
}
format_chars.next();
}
// Parse field width
let mut num_str = String::new();
while let Some(&c) = format_chars.peek() {
if !c.is_ascii_digit() {
break;
}
num_str.push(c);
format_chars.next();
}
if !num_str.is_empty() {
flags.field_width = num_str.parse().ok(); // Parse as integer
}
// Parse left precision (`#`)
if format_chars.peek() == Some(&'#') {
format_chars.next(); // Skip `#`
num_str.clear(); // Clear the string for precision
while let Some(&c) = format_chars.peek() {
if !c.is_ascii_digit() {
break;
}
num_str.push(c);
format_chars.next();
}
flags.left_precision = num_str.parse().ok(); // Parse as integer
}
// Parse right precision indicated by `.`
if format_chars.peek() == Some(&'.') {
format_chars.next(); // Skip `.`
num_str.clear(); // Clear the string for precision
while let Some(&c) = format_chars.peek() {
if !c.is_ascii_digit() {
break;
}
num_str.push(c);
format_chars.next();
}
flags.right_precision = num_str.parse().ok(); // Parse as integer
}
// Handle conversion specifiers (`i` or `n`)
match format_chars.next() {
Some('i') => {
// International formatting
flags.international = true;
let value = unsafe { args.next_arg::<f64>() }; // Get the argument as f64
if let Some(written) =
format_monetary(&mut buffer[pos..], value, &DEFAULT_MONETARY, &flags)
{
pos += written; // Update the position
} else {
return -1; // Formatting failed
}
}
Some('n') => {
// Locale-specific formatting
let value = unsafe { args.next_arg::<f64>() }; // Get the argument as f64
if let Some(written) =
format_monetary(&mut buffer[pos..], value, &DEFAULT_MONETARY, &flags)
{
pos += written; // Update the position
} else {
return -1; // Failed to format the value
}
}
_ => return -1,
}
}
// Ensure there is space for the null terminator
if pos >= buffer.len() {
return -1; // Buffer overflow
}
buffer[pos] = 0; // Null-terminate the buffer
pos as ssize_t // Return the number of characters written
}
/// Formats a monetary value into the given `buffer` using locale-specific rules
/// from `monetary` and formatting options from `flags`.
/// Returns `Some(len)` if successful, where `len` is the number of bytes written,
/// or `None` on error.
///
/// # Parameters
/// - `buffer`: The output slice in which to write the formatted string
/// - `value`: The numeric value to format as monetary
/// - `monetary`: Locale-specific formatting rules, including symbols, separators,
/// and grouping sizes
/// - `flags`: Additional formatting options
///
fn format_monetary(
buffer: &mut [u8],
value: f64,
monetary: &LocaleMonetaryInfo,
flags: &FormatFlags,
) -> Option<usize> {
// 1) determine sign and absolute value
let is_negative = value < 0.0;
let abs_value = fabs(value);
// 2) figure out how many fractionals digits to use
let frac_digits = if flags.international {
flags
.right_precision
.unwrap_or(monetary.int_frac_digits as usize)
} else {
flags
.right_precision
.unwrap_or(monetary.frac_digits as usize)
};
// 3) split the value into integer and fractional parts
let scale = pow(10.0, frac_digits as f64);
// Check for overflow
let max_safe_int = (i64::MAX as f64) / scale;
if abs_value >= max_safe_int {
return None;
}
let mut int_part = trunc(abs_value) as i64;
let mut frac_part = round((abs_value - int_part as f64) * scale) as i64;
// 4) handle carry-over if frac_part equals or exceeds scale after rounding
if frac_part >= scale as i64 {
int_part += 1;
frac_part = 0;
}
// 5) convert the integer part to string
let mut int_str = int_part.to_string();
// 6) apply left precision if specified (padding with '0')
// So if left_precision is 5 and int_str is "42", it becomes "00042".
if let Some(left_prec) = flags.left_precision {
if int_str.len() > left_prec {
// The integer part is too large to fit the precision
return None;
}
// Right-align the number in a field of `left_prec` width, padded with '0'
int_str = format!("{:0>width$}", int_str, width = left_prec);
}
// 7) build the final formatted output in a temporary String
let mut result = String::with_capacity(int_str.len() * 2 + 20);
// 7a) determine currency symbol placement and sign rules
let (cs_precedes, sep_by_space, sign_posn) = if is_negative {
(
monetary.n_cs_precedes,
monetary.n_sep_by_space,
monetary.n_sign_posn,
)
} else {
(
monetary.p_cs_precedes,
monetary.p_sep_by_space,
monetary.p_sign_posn,
)
};
// 7b) determine which sign to display
// - negative sign if value is negative
// - positive_sign if user forced sign
// - space if space_sign is set and value is positive
// - empty otherwise
let sign = match (is_negative, flags.force_sign, flags.space_sign) {
(true, _, _) => monetary.negative_sign,
(false, true, _) => monetary.positive_sign,
(false, false, true) => " ",
_ => "",
};
// 7c) choose which currency symbol to display
// - maybe empty if suppressed
// - int_curr_symbol for international format
// - currency_symbol for local format
let symbol = if flags.suppress_symbol {
""
} else if flags.international {
monetary.int_curr_symbol
} else {
monetary.currency_symbol
};
// 8) add opening parenthesis if sign position is 0
if sign_posn == 0 {
result.push('(');
} else if sign_posn == 1 {
result.push_str(sign);
}
// 9) add currency symbol if it precedes the amount
if cs_precedes {
result.push_str(symbol);
if sep_by_space {
result.push(' ');
}
}
// 10) group the integer string and append it
let grouped = apply_grouping(&int_str, monetary);
result.push_str(&grouped);
// 11) append the fractional part, if any
if frac_digits > 0 {
result.push_str(monetary.mon_decimal_point);
// Zero-pad fractional part to the specified width
result.push_str(&format!("{:0>width$}", frac_part, width = frac_digits));
}
// 12) if the currency symbol follows the amount, add it now
if !cs_precedes {
if sep_by_space {
result.push(' ');
}
result.push_str(symbol);
}
// 13) if sign_posn == 0, close the parenthesis
if sign_posn == 0 {
result.push(')');
}
// 14) checks if the user specified a total field width
// - if the final result is shorter, we padd it
// - if `left_justify` is true, padd on the right otherwise padd on the left
if let Some(width) = flags.field_width
&& result.len() < width
{
let padding = " ".repeat(width - result.len());
result = if flags.left_justify {
result + &padding
} else {
padding + &result
};
}
// 15) write the final string to the buffer
if result.len() > buffer.len() {
// Not enough space in the output buffer
return None;
}
buffer[..result.len()].copy_from_slice(result.as_bytes());
// 16) return how many bytes we wrote
Some(result.len())
}

Some files were not shown because too many files have changed in this diff Show More