0.3.0: converge relibc to upstream 0.6.0 + Red Bear patches
This commit is contained in:
+23
-10
@@ -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
@@ -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!();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
include_guard = "_RELIBC_PATHS_H"
|
||||
language = "C"
|
||||
style = "Type"
|
||||
no_includes = true
|
||||
cpp_compat = true
|
||||
@@ -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/";
|
||||
@@ -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::*;
|
||||
@@ -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/";
|
||||
@@ -3,7 +3,7 @@
|
||||
use platform::types::*;
|
||||
|
||||
/*
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn func(args) -> c_int {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
@@ -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!();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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,
|
||||
) {
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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() };
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
//! `bits/eventfd.h` — eventfd counter type.
|
||||
|
||||
use crate::platform::types::uint64_t;
|
||||
pub type eventfd_t = uint64_t;
|
||||
@@ -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
|
||||
@@ -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) {}
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
pub type locale_t = *mut core::ffi::c_void;
|
||||
@@ -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
|
||||
"""
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
use crate::platform::types::c_ushort;
|
||||
|
||||
pub type sa_family_t = c_ushort;
|
||||
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
use crate::platform::types::c_ulonglong;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type sigset_t = c_ulonglong;
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
pub type socklen_t = u32;
|
||||
@@ -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
|
||||
@@ -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) {}
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
include_guard = "_RELIBC_CRYPT_H"
|
||||
language = "C"
|
||||
style = "Type"
|
||||
no_includes = true
|
||||
cpp_compat = true
|
||||
|
||||
|
||||
[enum]
|
||||
prefix_with_name = true
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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(), ¶ms, &mut output).ok()?;
|
||||
|
||||
Some(format!(
|
||||
"$7${}${}${}",
|
||||
&setting[3..14],
|
||||
salt,
|
||||
Base64Bcrypt::encode_string(&output)
|
||||
))
|
||||
}
|
||||
@@ -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(), ¶ms)
|
||||
}
|
||||
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(), ¶ms)
|
||||
}
|
||||
} {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
"
|
||||
);
|
||||
|
||||
@@ -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
@@ -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) {}
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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,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
|
||||
|
||||
@@ -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
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
@@ -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()
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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) }
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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;
|
||||
@@ -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
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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) }
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user