//! `stdlib.h` implementation. //! //! See . use core::{convert::TryFrom, intrinsics, iter, mem, ptr, slice}; use rand::{ RngExt, SeedableRng, distr::{Alphanumeric, Distribution, Uniform}, }; use rand_jitter::JitterRng; use rand_xorshift::XorShiftRng; use crate::{ byte_literal::ByteLiteral, c_str::CStr, error::{Errno, ResultExt}, fs::File, header::{ ctype, errno::{self, *}, fcntl::*, limits, stdio::flush_io_streams, stdlib::sort::{QsortContext, QsortRContext}, string::*, sys_ioctl::*, time::constants::CLOCK_MONOTONIC, unistd::{self, _SC_PAGESIZE, sysconf}, wchar::*, }, ld_so, out::Out, platform::{ self, Pal, Sys, types::{ c_char, c_double, c_float, c_int, c_long, c_longlong, c_uint, c_ulong, c_ulonglong, c_ushort, c_void, size_t, ssize_t, uintptr_t, wchar_t, }, }, raw_cell::RawCell, sync::Once, }; mod rand48; mod random; mod sort; /// See . pub const EXIT_FAILURE: c_int = 1; /// See . pub const EXIT_SUCCESS: c_int = 0; /// See . pub const RAND_MAX: c_int = 2_147_483_647; /// See . //Maximum number of bytes in a multibyte character for the current locale pub const MB_CUR_MAX: c_int = 4; /// Actually specified for `limits.h`? //Maximum number of bytes in a multibyte characters for any locale pub const MB_LEN_MAX: c_int = 4; static ATEXIT_FUNCS: RawCell<[Option; 32]> = RawCell::new([None; 32]); static AT_QUICK_EXIT_FUNCS: RawCell<[Option; 32]> = RawCell::new([None; 32]); static L64A_BUFFER: RawCell<[c_char; 7]> = RawCell::new([0; 7]); // up to 6 digits plus null terminator static mut RNG: Option = None; // TODO: This could be const fn, but the trait system won't allow that. static RNG_SAMPLER: Once> = Once::new(); fn rng_sampler() -> &'static Uniform { RNG_SAMPLER.call_once(|| Uniform::new_inclusive(0, RAND_MAX).expect("within bounds")) } /// See . #[unsafe(no_mangle)] pub extern "C" fn _Exit(status: c_int) -> ! { unistd::_exit(status); } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn a64l(s: *const c_char) -> c_long { // Early return upon null pointer argument if s.is_null() { return 0; } // POSIX says only the low-order 32 bits are used. let mut l: i32 = 0; // Handle up to 6 input characters (excl. null terminator) for i in 0..6 { let digit_char = unsafe { *s.offset(i) }; let digit_value = match digit_char { 0 => break, // Null terminator encountered 46..=57 => { // ./0123456789 represents values 0 to 11. b'.' == 46 digit_char - 46 } 65..=90 => { // A-Z for values 12 to 37. b'A' == 65, 65-12 == 53 digit_char - 53 } 97..=122 => { // a-z for values 38 to 63. b'a' == 97, 97-38 == 59 digit_char - 59 } _ => return 0, // Early return for anything else }; l |= i32::from(digit_value) << (6 * i); } c_long::from(l) } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn abort() -> ! { log::error!("Abort"); intrinsics::abort(); } #[cfg(not(target_pointer_width = "64"))] #[unsafe(no_mangle)] static __stack_chk_guard: uintptr_t = 0x19fcadfe; #[cfg(target_pointer_width = "64")] #[unsafe(no_mangle)] static __stack_chk_guard: uintptr_t = 0xd048c37519fcadfe; #[unsafe(no_mangle)] unsafe extern "C" fn __stack_chk_fail() -> ! { unsafe { abort() }; } /// See . #[unsafe(no_mangle)] pub extern "C" fn abs(i: c_int) -> c_int { i.abs() } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn aligned_alloc(alignment: size_t, size: size_t) -> *mut c_void { if alignment == 0 || !size.is_multiple_of(alignment) { platform::ERRNO.set(EINVAL); return ptr::null_mut(); } /* The size-is-multiple-of-alignment requirement is the only * difference between aligned_alloc() and memalign(). */ #[allow(deprecated)] unsafe { memalign(alignment, size) } } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn at_quick_exit(func: Option) -> c_int { for i in 0..unsafe { AT_QUICK_EXIT_FUNCS.unsafe_ref().len() } { if unsafe { AT_QUICK_EXIT_FUNCS.unsafe_ref() }[i].is_none() { (unsafe { AT_QUICK_EXIT_FUNCS.unsafe_mut() })[i] = func; return 0; } } 1 } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn atexit(func: Option) -> c_int { for i in 0..unsafe { ATEXIT_FUNCS.unsafe_ref().len() } { if unsafe { ATEXIT_FUNCS.unsafe_ref() }[i].is_none() { (unsafe { ATEXIT_FUNCS.unsafe_mut() })[i] = func; return 0; } } 1 } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn atof(s: *const c_char) -> c_double { unsafe { strtod(s, ptr::null_mut()) } } macro_rules! dec_num_from_ascii { ($s:expr, $t:ty) => {{ let mut s = $s; // Iterate past whitespace while ctype::isspace(c_int::from(unsafe { *s })) != 0 { s = unsafe { s.offset(1) }; } // Find out if there is a - sign let neg_sign = match unsafe { *s } { 0x2d => { s = unsafe { s.offset(1) }; true } // '+' increment s and continue parsing 0x2b => { s = unsafe { s.offset(1) }; false } _ => false, }; let mut n: $t = 0; while ctype::isdigit(c_int::from(unsafe { *s })) != 0 { n = 10 * n - (unsafe { *s } as $t - 0x30); s = unsafe { s.offset(1) }; } if neg_sign { n } else { -n } }}; } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn atoi(s: *const c_char) -> c_int { dec_num_from_ascii!(s, c_int) } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn atol(s: *const c_char) -> c_long { dec_num_from_ascii!(s, c_long) } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn atoll(s: *const c_char) -> c_longlong { dec_num_from_ascii!(s, c_longlong) } unsafe extern "C" fn void_cmp(a: *const c_void, b: *const c_void) -> c_int { (unsafe { *(a.cast::()) }) - unsafe { *(b.cast::()) } as c_int } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn bsearch( key: *const c_void, base: *const c_void, nel: size_t, width: size_t, compar: Option c_int>, ) -> *mut c_void { let mut start = base; let mut len = nel; let cmp_fn = compar.unwrap_or(void_cmp); while len > 0 { let med = (start as size_t + (len >> 1) * width) as *const c_void; let diff = unsafe { cmp_fn(key, med) }; if diff == 0 { return med.cast_mut(); } else if diff > 0 { start = (med as usize + width) as *const c_void; len -= 1; } len >>= 1; } ptr::null_mut() } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn calloc(nelem: size_t, elsize: size_t) -> *mut c_void { //Handle possible integer overflow in size calculation match nelem.checked_mul(elsize) { Some(size) => { /* If allocation fails here, errno setting will be handled * by malloc() */ let ptr = unsafe { malloc(size) }; if !ptr.is_null() { unsafe { ptr.write_bytes(0, size) }; } ptr } None => { // For overflowing multiplication, we have to set errno here platform::ERRNO.set(ENOMEM); ptr::null_mut() } } } /// See . #[repr(C)] pub struct div_t { quot: c_int, rem: c_int, } /// See . #[unsafe(no_mangle)] pub extern "C" fn div(numer: c_int, denom: c_int) -> div_t { div_t { quot: numer / denom, rem: numer % denom, } } /// See . /// /// # Panics /// Panics if the function is unable to obtain a lock on the generator's global /// state. #[unsafe(no_mangle)] pub extern "C" fn drand48() -> c_double { let params = rand48::params(); let mut xsubi = rand48::xsubi_lock(); *xsubi = params.step(*xsubi); xsubi.get_f64() } /// See . /// /// # Deprecation /// The `ecvt()` function was marked as legacy in the Open Group Base /// Specifications Issue 6, and the function was removed in Issue 7. #[deprecated] // #[unsafe(no_mangle)] pub extern "C" fn ecvt( value: c_double, ndigit: c_int, decpt: *mut c_int, sign: *mut c_int, ) -> *mut c_char { unimplemented!(); } /// See . /// /// # Safety /// The caller must ensure that `xsubi` is convertible to a /// `&mut [c_ushort; 3]`. /// /// # Panics /// Panics if the function is unable to obtain a lock on the generator's global /// state. #[unsafe(no_mangle)] pub unsafe extern "C" fn erand48(xsubi: *mut c_ushort) -> c_double { let params = rand48::params(); let xsubi_mut: &mut [c_ushort; 3] = unsafe { slice::from_raw_parts_mut(xsubi, 3).try_into().unwrap() }; let new_xsubi_value = params.step(xsubi_mut.into()); *xsubi_mut = new_xsubi_value.into(); new_xsubi_value.get_f64() } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn exit(status: c_int) -> ! { unsafe extern "C" { static __fini_array_start: extern "C" fn(); static __fini_array_end: extern "C" fn(); } for i in (0..unsafe { ATEXIT_FUNCS.unsafe_ref().len() }).rev() { if let Some(func) = unsafe { ATEXIT_FUNCS.unsafe_ref() }[i] { (func)(); } } // Look for the neighbor functions in memory until the end let mut f = core::ptr::from_ref(unsafe { &__fini_array_end }); #[allow(clippy::op_ref)] while f > &raw const __fini_array_start { f = unsafe { f.sub(1) }; (unsafe { *f })(); } unsafe { ld_so::fini() }; unsafe { crate::pthread::terminate_from_main_thread() }; unsafe { flush_io_streams() }; Sys::exit(status); } /// See . /// /// # Deprecation /// The `fcvt()` function was marked as legacy in the Open Group Base /// Specifications Issue 6, and the function was removed in Issue 7. #[deprecated] // #[unsafe(no_mangle)] pub extern "C" fn fcvt( value: c_double, ndigit: c_int, decpt: *mut c_int, sign: *mut c_int, ) -> *mut c_char { unimplemented!(); } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn free(ptr: *mut c_void) { unsafe { platform::free(ptr) }; } /// See . /// /// # Deprecation /// The `gcvt()` function was marked as legacy in the Open Group Base /// Specifications Issue 6, and the function was removed in Issue 7. #[deprecated] // #[unsafe(no_mangle)] pub extern "C" fn gcvt(value: c_double, ndigit: c_int, buf: *mut c_char) -> *mut c_char { unimplemented!(); } unsafe fn find_env(search: *const c_char) -> Option<(usize, *mut c_char)> { for (i, mut item) in platform::environ_iter().enumerate() { let mut search = search; loop { let end_of_query = unsafe { *search } == 0 || unsafe { *search } == ByteLiteral::cast_cchar(b'='); if unsafe { *item } == 0 { //TODO: environ has an item without value, is this a problem? break; } if unsafe { *item } == ByteLiteral::cast_cchar(b'=') || end_of_query { if unsafe { *item } == ByteLiteral::cast_cchar(b'=') && end_of_query { // Both keys env here return Some((i, unsafe { item.add(1) })); } else { break; } } if unsafe { *item } != unsafe { *search } { break; } item = unsafe { item.add(1) }; search = unsafe { search.add(1) }; } } None } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn getenv(name: *const c_char) -> *mut c_char { unsafe { find_env(name) } .map(|val| val.1) .unwrap_or(ptr::null_mut()) } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn getsubopt( optionp: *mut *mut c_char, keylistp: *const *mut c_char, valuep: *mut *mut c_char, ) -> c_int { if optionp.is_null() || (unsafe { *optionp }).is_null() || keylistp.is_null() || valuep.is_null() { return -1; } let start = unsafe { *optionp }; let mut cursor = start; let mut found_comma = false; while unsafe { *cursor } != 0 { if unsafe { *cursor } == ByteLiteral::cast_cchar(b',') { unsafe { *cursor = 0 }; unsafe { *optionp = cursor.add(1) }; found_comma = true; break; } cursor = unsafe { cursor.add(1) }; } if !found_comma { unsafe { *optionp = cursor }; } let mut i = 0; while !(unsafe { *keylistp.add(i) }).is_null() { let token = unsafe { *keylistp.add(i) }; let token_len = unsafe { strlen(token) }; if unsafe { strncmp(start, token, token_len) } == 0 { let suffix_char = unsafe { *start.add(token_len) }; if suffix_char == ByteLiteral::cast_cchar(b'=') { unsafe { *valuep = start.add(token_len + 1) }; return i as c_int; } else if suffix_char == 0 { unsafe { *valuep = ptr::null_mut() }; return i as c_int; } } i += 1; } unsafe { *valuep = start }; -1 } /// See . #[unsafe(no_mangle)] pub extern "C" fn grantpt(fildes: c_int) -> c_int { // No-op on Linux and Redox 0 } /// See . // Ported from musl #[unsafe(no_mangle)] pub unsafe extern "C" fn initstate(seed: c_uint, state: *mut c_char, size: size_t) -> *mut c_char { if size < 8 { ptr::null_mut() } else { let mut random_state = random::state_lock(); let old_state = unsafe { random_state.save() }; random_state.n = match size { 0..=7 => unreachable!(), // ensured above 8..=31 => 0, 32..=63 => 7, 64..=127 => 15, 128..=255 => 31, _ => 63, }; random_state.x_ptr = unsafe { (state.cast::<[u8; 4]>()).add(1) }; unsafe { random_state.seed(seed) }; unsafe { random_state.save() }; old_state.cast::<_>() } } /// See . /// /// # Safety /// The caller must ensure that `xsubi` is convertible to a /// `&mut [c_ushort; 3]`. /// /// # Panics /// Panics if the function is unable to obtain a lock on the generator's global /// state. #[unsafe(no_mangle)] pub unsafe extern "C" fn jrand48(xsubi: *mut c_ushort) -> c_long { let params = rand48::params(); let xsubi_mut: &mut [c_ushort; 3] = unsafe { slice::from_raw_parts_mut(xsubi, 3) } .try_into() .unwrap(); let new_xsubi_value = params.step(xsubi_mut.into()); *xsubi_mut = new_xsubi_value.into(); new_xsubi_value.get_i32() } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn l64a(value: c_long) -> *mut c_char { // POSIX says we should only consider the lower 32 bits of value. let value_as_i32 = value as i32; /* If we pretend to extend the 32-bit value with 4 binary zeros, we * would get a 36-bit integer. The number of base-64 digits to be * left unused can then be found by taking the number of leading * zeros, dividing by 6 and rounding down (i.e. using integer * division). */ let num_output_digits = usize::try_from(6 - (value_as_i32.leading_zeros() + 4) / 6).unwrap(); // Reset buffer (and have null terminator in place for any result) unsafe { L64A_BUFFER.unsafe_set([0; 7]) }; for i in 0..num_output_digits { // Conversion to c_char always succeeds for the range 0..=63 let digit_value = c_char::try_from((value_as_i32 >> (6 * i)) & 63).unwrap(); (unsafe { L64A_BUFFER.unsafe_mut() })[i] = match digit_value { 0..=11 => { // ./0123456789 for values 0 to 11. b'.' == 46 46 + digit_value } 12..=37 => { // A-Z for values 12 to 37. b'A' == 65, 65-12 == 53 53 + digit_value } 38..=63 => { // a-z for values 38 to 63. b'a' == 97, 97-38 == 59 59 + digit_value } _ => unreachable!(), // Guaranteed by taking "& 63" above }; } unsafe { L64A_BUFFER.unsafe_mut().as_mut_ptr() } } /// See . #[unsafe(no_mangle)] pub extern "C" fn labs(i: c_long) -> c_long { i.abs() } /// See . /// /// # Safety /// The caller must ensure that `param` is convertible to a /// `&mut [c_ushort; 7]`. /// /// # Panics /// Panics if the function is unable to obtain a lock on the generator's global /// state. #[unsafe(no_mangle)] pub unsafe extern "C" fn lcong48(param: *mut c_ushort) { let mut xsubi = rand48::xsubi_lock(); let mut params = rand48::params_mut(); let param_slice = unsafe { slice::from_raw_parts(param, 7) }; let xsubi_ref: &[c_ushort; 3] = param_slice[0..3].try_into().unwrap(); let a_ref: &[c_ushort; 3] = param_slice[3..6].try_into().unwrap(); let c = param_slice[6]; *xsubi = xsubi_ref.into(); params.set(a_ref, c); } /// See . #[repr(C)] pub struct ldiv_t { quot: c_long, rem: c_long, } /// See . #[unsafe(no_mangle)] pub extern "C" fn ldiv(numer: c_long, denom: c_long) -> ldiv_t { ldiv_t { quot: numer / denom, rem: numer % denom, } } /// See . #[unsafe(no_mangle)] pub extern "C" fn llabs(i: c_longlong) -> c_longlong { i.abs() } /// See . #[repr(C)] pub struct lldiv_t { quot: c_longlong, rem: c_longlong, } /// See . #[unsafe(no_mangle)] pub extern "C" fn lldiv(numer: c_longlong, denom: c_longlong) -> lldiv_t { lldiv_t { quot: numer / denom, rem: numer % denom, } } /// See . /// /// # Panics /// Panics if the function is unable to obtain a lock on the generator's global /// state. #[unsafe(no_mangle)] pub extern "C" fn lrand48() -> c_long { let params = rand48::params(); let mut xsubi = rand48::xsubi_lock(); *xsubi = params.step(*xsubi); xsubi.get_u31() } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn malloc(size: size_t) -> *mut c_void { let ptr = unsafe { platform::alloc(size) }; if ptr.is_null() { platform::ERRNO.set(ENOMEM); } ptr } /// Non-POSIX, see . #[deprecated] #[unsafe(no_mangle)] pub unsafe extern "C" fn memalign(alignment: size_t, size: size_t) -> *mut c_void { if alignment.is_power_of_two() { let ptr = unsafe { platform::alloc_align(size, alignment) }; if ptr.is_null() { platform::ERRNO.set(ENOMEM); } ptr } else { platform::ERRNO.set(EINVAL); ptr::null_mut() } } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn mblen(s: *const c_char, n: size_t) -> c_int { let mut wc: wchar_t = 0; let mut state: mbstate_t = mbstate_t {}; let result: usize = unsafe { mbrtowc(&raw mut wc, s, n, &raw mut state) }; if result == -1isize as usize { return -1; } if result == -2isize as usize { return -1; } result as i32 } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn mbstowcs(pwcs: *mut wchar_t, mut s: *const c_char, n: size_t) -> size_t { let mut state: mbstate_t = mbstate_t {}; unsafe { mbsrtowcs(pwcs, &raw mut s, n, &raw mut state) } } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn mbtowc(pwc: *mut wchar_t, s: *const c_char, n: size_t) -> c_int { let mut state: mbstate_t = mbstate_t {}; (unsafe { mbrtowc(pwc, s, n, &raw mut state) }) as c_int } fn inner_mktemp(name: *mut c_char, suffix_len: c_int, mut attempt: F) -> Option where F: FnMut() -> Option, { let len = unsafe { strlen(name) as c_int }; if len < 6 || suffix_len > len - 6 { platform::ERRNO.set(errno::EINVAL); return None; } for i in (len - suffix_len - 6)..(len - suffix_len) { if unsafe { *name.offset(i as isize) } != ByteLiteral::cast_cchar(b'X') { platform::ERRNO.set(errno::EINVAL); return None; } } let mut rng = JitterRng::new_with_timer(get_nstime); let _ = rng.test_timer(); for _ in 0..100 { let char_iter = iter::repeat(()) .map(|()| rng.sample(Alphanumeric)) .take(6) .enumerate(); unsafe { for (i, c) in char_iter { *name.offset((len as isize) - (suffix_len as isize) - (i as isize) - 1) = c as c_char } } if let result @ Some(_) = attempt() { return result; } } platform::ERRNO.set(errno::EEXIST); None } fn get_nstime() -> u64 { unsafe { let mut ts = mem::MaybeUninit::uninit(); if Sys::clock_gettime(CLOCK_MONOTONIC, Out::from_uninit_mut(&mut ts)).is_ok() {}; // TODO what to do if Err? ts.assume_init().tv_nsec as u64 } } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn mkdtemp(name: *mut c_char) -> *mut c_char { inner_mktemp(name, 0, || { let name_c = unsafe { CStr::from_ptr(name) }; match Sys::mkdir(name_c, 0o700) { Ok(()) => Some(name), Err(_) => None, } }) .unwrap_or(ptr::null_mut()) } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn mkostemp(name: *mut c_char, flags: c_int) -> c_int { unsafe { mkostemps(name, 0, flags) } } /// Non-POSIX, see . #[unsafe(no_mangle)] pub unsafe extern "C" fn mkostemps( name: *mut c_char, suffix_len: c_int, mut flags: c_int, ) -> c_int { // TODO: Rustify impl flags &= !O_ACCMODE; flags |= O_RDWR | O_CREAT | O_EXCL; inner_mktemp(name, suffix_len, || { let name = unsafe { CStr::from_ptr(name) }; let fd = Sys::open(name, flags, 0o600).or_minus_one_errno(); if fd >= 0 { Some(fd) } else { None } }) .unwrap_or(-1) } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn mkstemp(name: *mut c_char) -> c_int { unsafe { mkostemps(name, 0, 0) } } /// Non-POSIX, see . #[unsafe(no_mangle)] pub unsafe extern "C" fn mkstemps(name: *mut c_char, suffix_len: c_int) -> c_int { unsafe { mkostemps(name, suffix_len, 0) } } /// See . /// /// # Deprecation /// The `mktemp()` function was marked as legacy in the Open Group Base /// Specifications Issue 6, and the function was removed in Issue 7. #[deprecated] #[unsafe(no_mangle)] pub unsafe extern "C" fn mktemp(name: *mut c_char) -> *mut c_char { if inner_mktemp(name, 0, || { let name = unsafe { CStr::from_ptr(name) }; if Sys::access(name, 0) == Err(Errno(ENOENT)) { Some(()) } else { None } }) .is_none() { unsafe { *name = 0 }; } name } /// See . /// /// # Panics /// Panics if the function is unable to obtain a lock on the generator's global /// state. #[unsafe(no_mangle)] pub extern "C" fn mrand48() -> c_long { let params = rand48::params(); let mut xsubi = rand48::xsubi_lock(); *xsubi = params.step(*xsubi); xsubi.get_i32() } /// See . /// /// # Safety /// The caller must ensure that `xsubi` is convertible to a /// `&mut [c_ushort; 3]`. /// /// # Panics /// Panics if the function is unable to obtain a lock on the generator's global /// state. #[unsafe(no_mangle)] pub unsafe extern "C" fn nrand48(xsubi: *mut c_ushort) -> c_long { let params = rand48::params(); let xsubi_mut: &mut [c_ushort; 3] = unsafe { slice::from_raw_parts_mut(xsubi, 3) } .try_into() .unwrap(); let new_xsubi_value = params.step(xsubi_mut.into()); *xsubi_mut = new_xsubi_value.into(); new_xsubi_value.get_u31() } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn posix_memalign( memptr: *mut *mut c_void, alignment: size_t, size: size_t, ) -> c_int { const VOID_PTR_SIZE: usize = mem::size_of::<*mut c_void>(); if alignment.is_multiple_of(VOID_PTR_SIZE) && alignment.is_power_of_two() { let ptr = unsafe { platform::alloc_align(size, alignment) }; unsafe { *memptr = ptr }; if ptr.is_null() { ENOMEM } else { 0 } } else { unsafe { *memptr = ptr::null_mut() }; EINVAL } } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn posix_openpt(flags: c_int) -> c_int { #[cfg(target_os = "redox")] let r = unsafe { open(c"/scheme/pty/ptmx".as_ptr(), flags) }; #[cfg(target_os = "linux")] let r = unsafe { open(c"/dev/ptmx".as_ptr(), flags) }; if r < 0 && platform::ERRNO.get() == ENOSPC { platform::ERRNO.set(EAGAIN); } r } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn ptsname(fd: c_int) -> *mut c_char { const PTS_BUFFER_LEN: usize = limits::TTY_NAME_MAX as usize; static mut PTS_BUFFER: [c_char; PTS_BUFFER_LEN] = [0; PTS_BUFFER_LEN]; let ret = unsafe { ptsname_r(fd, (&raw mut PTS_BUFFER).cast(), PTS_BUFFER_LEN) }; if ret != 0 { platform::ERRNO.set(ret); ptr::null_mut() } else { (&raw mut PTS_BUFFER).cast() } } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn ptsname_r(fd: c_int, buf: *mut c_char, buflen: size_t) -> c_int { if buf.is_null() { platform::ERRNO.set(EINVAL); EINVAL } else { unsafe { __ptsname_r(fd, buf, buflen) } } } // ptsname_r is not allowed to set errno, but it has it as a return value. #[inline(always)] unsafe fn __ptsname_r(fd: c_int, buf: *mut c_char, buflen: size_t) -> c_int { let mut pty: c_int = 0; if unsafe { ioctl(fd, TIOCGPTN, ptr::from_mut(&mut pty).cast::()) } == 0 { // Linux and Redox use different resource names for PTS's. #[cfg(target_os = "linux")] let name = format!("/dev/pts/{}", pty); #[cfg(target_os = "redox")] let name = format!("/scheme/pty/{}", pty); let len = name.len(); // We need + 1 to account for the NUL terminator. if len + 1 > buflen { ERANGE } else { // we have checked the string will fit in the buffer // so can use strcpy safely let s = name.as_ptr().cast(); unsafe { ptr::copy_nonoverlapping(s, buf, len) }; // NUL-terminate the result. unsafe { *(buf.add(len + 1)) = 0 }; 0 } } else { platform::ERRNO.get() } } unsafe fn put_new_env(insert: *mut c_char) { // XXX: Another problem is that `environ` can be set to any pointer, which means there is a // chance of a memory leak. But we can check if it was the same as before, like musl does. if unsafe { platform::environ } == unsafe { platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr() } { { let our_environ = unsafe { &mut *platform::OUR_ENVIRON.as_mut_ptr() }; *our_environ.last_mut().unwrap() = insert; our_environ.push(core::ptr::null_mut()); } // Likely a no-op but is needed due to Stacked Borrows. unsafe { platform::environ = platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr() }; } else { { let our_environ = unsafe { &mut *platform::OUR_ENVIRON.as_mut_ptr() }; our_environ.clear(); our_environ.extend(platform::environ_iter()); our_environ.push(insert); our_environ.push(core::ptr::null_mut()); } unsafe { platform::environ = platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr() }; } } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn putenv(insert: *mut c_char) -> c_int { assert_ne!(insert, ptr::null_mut(), "putenv(NULL)"); if let Some((i, _)) = unsafe { find_env(insert) } { // XXX: The POSIX manual states that environment variables can be *set* via the `environ` // global variable. While we can check if a pointer belongs to our allocator, or check // `environ` against a vector which we control, it is likely not worth the effort. unsafe { platform::environ.add(i).write(insert) }; } else { unsafe { put_new_env(insert) }; } 0 } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn qsort( base: *mut c_void, nel: size_t, width: size_t, compar: Option c_int>, ) { if let Some(comp) = compar { // XXX: check width too? not specified if nel > 0 { // XXX: maybe try to do mergesort/timsort first and fallback to introsort if memory // allocation fails? not sure what is ideal let mut ctx = QsortContext { comp }; unsafe { sort::introsort(base.cast::(), nel, width, &mut ctx) }; } } } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn qsort_r( base: *mut c_void, nel: size_t, width: size_t, compar: Option c_int>, arg: *mut c_void, ) { if let (Some(comp), true) = (compar, nel > 0) { let mut ctx = QsortRContext { comp, arg }; unsafe { sort::introsort(base.cast::(), nel, width, &mut ctx) }; } } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn quick_exit(status: c_int) -> ! { for i in (0..unsafe { AT_QUICK_EXIT_FUNCS.unsafe_ref() }.len()).rev() { if let Some(func) = unsafe { AT_QUICK_EXIT_FUNCS.unsafe_ref() }[i] { (func)(); } } Sys::exit(status); } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn rand() -> c_int { unsafe { match RNG { Some(ref mut rng) => rng_sampler().sample(rng), None => { let mut rng = XorShiftRng::from_seed([1; 16]); let ret = rng_sampler().sample(&mut rng); RNG = Some(rng); ret } } } } /// See . /// /// # Deprecation /// The `rand_r()` function was marked as obsolescent in the Open Group Base /// Specifications Issue 7, and the function was removed in Issue 8. #[deprecated] #[unsafe(no_mangle)] pub unsafe extern "C" fn rand_r(seed: *mut c_uint) -> c_int { if seed.is_null() { errno::EINVAL } else { // set the type explicitly so this will fail if the array size for XorShiftRng changes let seed_arr: [u8; 16] = unsafe { mem::transmute([*seed; 16 / mem::size_of::()]) }; let mut rng = XorShiftRng::from_seed(seed_arr); let ret = rng_sampler().sample(&mut rng); unsafe { *seed = ret as _ }; ret } } /// See . // Ported from musl #[unsafe(no_mangle)] pub unsafe extern "C" fn random() -> c_long { let mut random_state = random::state_lock(); let k: u32; unsafe { random_state.ensure_x_ptr_init() }; if random_state.n == 0 { let x_old = u32::from_ne_bytes(unsafe { *random_state.x_ptr }); let x_new = random::lcg31_step(x_old); unsafe { *random_state.x_ptr = x_new.to_ne_bytes() }; k = x_new; } else { // The non-u32-aligned way of saying x[i] += x[j]... let x_i_old = u32::from_ne_bytes(unsafe { *random_state.x_ptr.add(usize::from(random_state.i)) }); let x_j = u32::from_ne_bytes(unsafe { *random_state.x_ptr.add(usize::from(random_state.j)) }); let x_i_new = x_i_old.wrapping_add(x_j); unsafe { *random_state.x_ptr.add(usize::from(random_state.i)) = x_i_new.to_ne_bytes() }; k = x_i_new >> 1; random_state.i += 1; if random_state.i == random_state.n { random_state.i = 0; } random_state.j += 1; if random_state.j == random_state.n { random_state.j = 0; } } /* Both branches of this function result in a "u31", which will * always fit in a c_long. */ #[cfg(not(target_arch = "x86"))] return c_long::from(k); #[cfg(target_arch = "x86")] // c_long on x86 is i32 so not infallible cast c_long::try_from(k).unwrap() } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn realloc(ptr: *mut c_void, size: size_t) -> *mut c_void { let new_ptr = unsafe { platform::realloc(ptr, size) }; if new_ptr.is_null() { platform::ERRNO.set(ENOMEM); } new_ptr } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn reallocarray(ptr: *mut c_void, m: size_t, n: size_t) -> *mut c_void { //Handle possible integer overflow in size calculation match m.checked_mul(n) { Some(size) => unsafe { realloc(ptr, size) }, None => { // For overflowing multiplication, we have to set errno here platform::ERRNO.set(ENOMEM); ptr::null_mut() } } } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn realpath(pathname: *const c_char, resolved: *mut c_char) -> *mut c_char { let ptr = if resolved.is_null() { (unsafe { malloc(limits::PATH_MAX) }).cast::() } else { resolved }; let out = unsafe { slice::from_raw_parts_mut(ptr.cast::(), limits::PATH_MAX) }; { let file = match File::open(unsafe { CStr::from_ptr(pathname) }, O_PATH | O_CLOEXEC) { Ok(file) => file, Err(_) => return ptr::null_mut(), }; let len = out.len(); // TODO: better error handling let read = Sys::fpath(*file, &mut out[..len - 1]) .map(|read| read as ssize_t) .or_minus_one_errno(); if read < 0 { return ptr::null_mut(); } out[read as usize] = 0; } ptr } /// See . // #[unsafe(no_mangle)] pub unsafe extern "C" fn secure_getenv(name: *const c_char) -> *mut c_char { unimplemented!(); } /// See . /// /// # Safety /// The caller must ensure that `seed16v` is convertible to a `&[c_ushort; 3]`. /// Additionally, the caller must ensure that the function has exclusive access /// to the static buffer it returns; this includes avoiding simultaneous calls /// to this function. /// /// # Panics /// Panics if the function is unable to obtain a lock on the generator's global /// state. #[unsafe(no_mangle)] pub unsafe extern "C" fn seed48(seed16v: *mut c_ushort) -> *mut c_ushort { static RETURN_BUFFER: RawCell<[c_ushort; 3]> = RawCell::new([0; 3]); let mut params = rand48::params_mut(); let mut xsubi = rand48::xsubi_lock(); // SAFETY: the caller is required to ensure seed16v is convertible to // &[c_ushort; 3]. let seed16v_ref: &[c_ushort; 3] = unsafe { slice::from_raw_parts(seed16v, 3) } .try_into() .unwrap(); // SAFETY: the caller is required to ensure exclusive access to // RETURN_BUFFER. let return_buffer_mut = unsafe { RETURN_BUFFER.unsafe_mut() }; *return_buffer_mut = (*xsubi).into(); *xsubi = seed16v_ref.into(); params.reset(); RETURN_BUFFER.as_mut_ptr().cast() } unsafe fn copy_kv( existing: *mut c_char, key: *const c_char, value: *const c_char, key_len: usize, value_len: usize, ) { unsafe { core::ptr::copy_nonoverlapping(key, existing, key_len) }; unsafe { core::ptr::write(existing.add(key_len), ByteLiteral::cast_cchar(b'=')) }; unsafe { core::ptr::copy_nonoverlapping(value, existing.add(key_len + 1), value_len) }; unsafe { core::ptr::write(existing.add(key_len + 1 + value_len), 0) }; } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn setenv( key: *const c_char, value: *const c_char, overwrite: c_int, ) -> c_int { let key_len = unsafe { strlen(key) }; let value_len = unsafe { strlen(value) }; if let Some((i, existing)) = unsafe { find_env(key) } { if overwrite == 0 { return 0; } let existing_len = unsafe { strlen(existing) }; if existing_len >= value_len { // Reuse existing element's allocation unsafe { core::ptr::copy_nonoverlapping(value, existing, value_len) }; //TODO: fill to end with zeroes unsafe { core::ptr::write(existing.add(value_len), 0) }; } else { // Reuse platform::environ slot, but allocate a new pointer. let ptr = unsafe { platform::alloc(key_len as usize + 1 + value_len as usize + 1) } .cast::(); unsafe { copy_kv(ptr, key, value, key_len, value_len) }; unsafe { platform::environ.add(i).write(ptr) }; } } else { // Expand platform::environ and allocate a new pointer. let ptr = unsafe { platform::alloc(key_len as usize + 1 + value_len as usize + 1) } .cast::(); unsafe { copy_kv(ptr, key, value, key_len, value_len) }; unsafe { put_new_env(ptr) }; } //platform::free(platform::inner_environ[index] as *mut c_void); 0 } /// See . /// /// # Deprecation /// The `setkey()` function was marked as obsolescent in the Open Group Base /// Specifications Issue 8. #[deprecated] // #[unsafe(no_mangle)] pub unsafe extern "C" fn setkey(key: *const c_char) { unimplemented!(); } /// See . // Ported from musl. The state parameter is no longer const in newer versions of POSIX. #[unsafe(no_mangle)] pub unsafe extern "C" fn setstate(state: *mut c_char) -> *mut c_char { let mut random_state = random::state_lock(); let old_state = unsafe { random_state.save() }; unsafe { random_state.load(state.cast::<_>()) }; old_state.cast::<_>() } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn srand(seed: c_uint) { unsafe { RNG = Some(XorShiftRng::from_seed([seed as u8; 16])) }; } /// See . /// /// # Panics /// Panics if the function is unable to obtain a lock on the generator's global /// state. #[unsafe(no_mangle)] pub extern "C" fn srand48(seedval: c_long) { let mut params = rand48::params_mut(); let mut xsubi = rand48::xsubi_lock(); params.reset(); /* Set the high 32 bits of the 48-bit X_i value to the lower 32 bits * of the input argument, and the lower 16 bits to 0x330e, as * specified in POSIX. */ *xsubi = ((u64::from(seedval as u32) << 16) | 0x330e) .try_into() .unwrap(); } /// See . // Ported from musl #[unsafe(no_mangle)] pub unsafe extern "C" fn srandom(seed: c_uint) { let mut random_state = random::state_lock(); unsafe { random_state.seed(seed) }; } pub fn is_positive(ch: c_char) -> Option<(bool, isize)> { match ch { 0 => None, ch if ch == ByteLiteral::cast_cchar(b'+') => Some((true, 1)), ch if ch == ByteLiteral::cast_cchar(b'-') => Some((false, 1)), _ => Some((true, 0)), } } pub unsafe fn detect_base(s: *const c_char) -> Option<(c_int, isize)> { let first = unsafe { *s } as u8; match first { 0 => None, b'0' => { let second = unsafe { *s.add(1) } as u8; if second == b'X' || second == b'x' { Some((16, 2)) } else if (b'0'..=b'7').contains(&second) { Some((8, 1)) } else { // in this case, the prefix (0) is going to be the number Some((8, 0)) } } _ => Some((10, 0)), } } pub unsafe fn convert_octal(s: *const c_char) -> Option<(c_ulong, isize, bool)> { if unsafe { *s } != 0 && unsafe { *s } == ByteLiteral::cast_cchar(b'0') { if let Some((val, idx, overflow)) = unsafe { convert_integer(s.add(1), 8) } { Some((val, idx + 1, overflow)) } else { // in case the prefix is not actually a prefix Some((0, 1, false)) } } else { None } } pub unsafe fn convert_hex(s: *const c_char) -> Option<(c_ulong, isize, bool)> { if (unsafe { *s } != 0 && unsafe { *s } == ByteLiteral::cast_cchar(b'0')) && (unsafe { *s.add(1) } != 0 && (unsafe { *s.add(1) } == ByteLiteral::cast_cchar(b'x') || unsafe { *s.add(1) } == ByteLiteral::cast_cchar(b'X'))) { unsafe { convert_integer(s.add(2), 16) } .map(|(val, idx, overflow)| (val, idx + 2, overflow)) } else { unsafe { convert_integer(s, 16) } } } pub unsafe fn convert_integer(s: *const c_char, base: c_int) -> Option<(c_ulong, isize, bool)> { // -1 means the character is invalid #[rustfmt::skip] const LOOKUP_TABLE: [c_long; 256] = [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ]; let mut num: c_ulong = 0; let mut idx = 0; let mut overflowed = false; loop { // `-1 as usize` is usize::MAX // `-1 as u8 as usize` is u8::MAX // It extends by the sign bit unless we cast it to unsigned first. let val = LOOKUP_TABLE[unsafe { *s.offset(idx) } as u8 as usize]; if val == -1 || val as c_int >= base { break; } else { if let Some(res) = num .checked_mul(base as c_ulong) .and_then(|num| num.checked_add(val as c_ulong)) { num = res; } else { platform::ERRNO.set(ERANGE); num = c_ulong::MAX; overflowed = true; } idx += 1; } } if idx > 0 { Some((num, idx, overflowed)) } else { None } } /// See . #[expect(clippy::cast_lossless)] // not all users of `strto_float_impl!` are lossless #[unsafe(no_mangle)] pub unsafe extern "C" fn strtod(s: *const c_char, endptr: *mut *mut c_char) -> c_double { strto_float_impl!(c_double, s, endptr) } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn strtof(s: *const c_char, endptr: *mut *mut c_char) -> c_float { strto_float_impl!(c_float, s, endptr) } /// See . /// /// Converts the initial portion of the string pointed to by `nptr` to a type /// `long`. /// /// Upon success, returns the converted value. If no conversion could be /// performed or the value of `base` is not supported, returns `0`. #[unsafe(no_mangle)] pub unsafe extern "C" fn strtol( nptr: *const c_char, endptr: *mut *mut c_char, base: c_int, ) -> c_long { strto_impl!(c_long, true, c_long::MAX, c_long::MIN, nptr, endptr, base) } // TODO: strtold(), when long double is available /// See . /// /// Converts the initial portion of the string pointed to by `nptr` to a type /// `long long`. /// /// Upon success, returns the converted value. If no conversion could be /// performed or the value of `base` is not supported, returns `0`. #[unsafe(no_mangle)] pub unsafe extern "C" fn strtoll( nptr: *const c_char, endptr: *mut *mut c_char, base: c_int, ) -> c_longlong { strto_impl!( c_longlong, true, c_longlong::MAX, c_longlong::MIN, nptr, endptr, base ) } /// See . /// /// Converts the initial portion of the string pointed to by `str` to a type /// `unsigned long`. /// /// Upon success, returns the converted value. If no conversion could be /// performed or the value of `base` is not supported, returns `0`. #[unsafe(no_mangle)] pub unsafe extern "C" fn strtoul( str: *const c_char, endptr: *mut *mut c_char, base: c_int, ) -> c_ulong { strto_impl!( c_ulong, false, c_ulong::MAX, c_ulong::MIN, str, endptr, base ) } /// See . /// /// Converts the initial portion of the string pointed to by `str` to a type /// `unsigned long long`. /// /// Upon success, returns the converted value. If no conversion could be /// performed or the value of `base` is not supported, returns `0`. #[unsafe(no_mangle)] pub unsafe extern "C" fn strtoull( str: *const c_char, endptr: *mut *mut c_char, base: c_int, ) -> c_ulonglong { strto_impl!( c_ulonglong, false, c_ulonglong::MAX, c_ulonglong::MIN, str, endptr, base ) } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn system(command: *const c_char) -> c_int { // TODO: rusty error handling? //TODO: share code with popen // handle shell detection on command == NULL if command.is_null() { let status = unsafe { system(c"exit 0".as_ptr().cast::()) }; if status == 0 { return 1; } else { return 0; } } let child_pid = unsafe { unistd::fork() }; if child_pid == 0 { let command_nonnull = command.cast::(); let shell = c"/bin/sh".as_ptr(); let args = [c"sh".as_ptr(), c"-c".as_ptr(), command_nonnull, ptr::null()]; unsafe { unistd::execv(shell.cast::(), args.as_ptr().cast::<*mut c_char>()) }; unsafe { exit(127) }; unreachable!(); } else if child_pid > 0 { let mut wstatus = 0; if Sys::waitpid(child_pid, Some(Out::from_mut(&mut wstatus)), 0).or_minus_one_errno() == -1 { return -1; } wstatus } else { -1 } } /// See . /// /// # Deprecation /// The `ttyslot()` function was marked as obsolescent in the Open Group Base /// Specifications Issue 5, and the function was removed in Issue 6. #[deprecated] // #[unsafe(no_mangle)] pub extern "C" fn ttyslot() -> c_int { unimplemented!(); } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn unlockpt(fildes: c_int) -> c_int { let mut u: c_int = 0; unsafe { ioctl( fildes, TIOCSPTLCK, ptr::from_mut::(&mut u).cast::(), ) } } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn unsetenv(key: *const c_char) -> c_int { if let Some((i, _)) = unsafe { find_env(key) } { if unsafe { platform::environ } == unsafe { platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr() } { // No need to worry about updating the pointer, this does not // reallocate in any way. And the final null is already shifted back. { let our_environ = unsafe { &mut *platform::OUR_ENVIRON.as_mut_ptr() }; our_environ.remove(i); } // My UB paranoia. unsafe { platform::environ = platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr() }; } else { { let our_environ = unsafe { &mut *platform::OUR_ENVIRON.as_mut_ptr() }; our_environ.clear(); our_environ.extend( platform::environ_iter() .enumerate() .filter(|&(j, _)| j != i) .map(|(_, v)| v), ); our_environ.push(core::ptr::null_mut()); } unsafe { platform::environ = platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr() }; } } 0 } /// See . /// /// # Deprecation /// The `valloc()` function was marked as obsolescent in the Open Group Base /// Specifications Issue 5, and the function was removed in Issue 6. #[deprecated] #[unsafe(no_mangle)] pub unsafe extern "C" fn valloc(size: size_t) -> *mut c_void { /* sysconf(_SC_PAGESIZE) is a c_long and may in principle not * convert correctly to a size_t. */ match size_t::try_from(unsafe { sysconf(_SC_PAGESIZE) }) { Ok(page_size) => { /* valloc() is not supposed to be able to set errno to * EINVAL, hence no call to memalign(). */ let ptr = unsafe { platform::alloc_align(size, page_size) }; if ptr.is_null() { platform::ERRNO.set(ENOMEM); } ptr } Err(_) => { // A corner case. No errno setting. ptr::null_mut() } } } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn wcstombs(s: *mut c_char, mut pwcs: *const wchar_t, n: size_t) -> size_t { let mut state: mbstate_t = mbstate_t {}; unsafe { wcsrtombs(s, &raw mut pwcs, n, &raw mut state) } } /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn wctomb(s: *mut c_char, wc: wchar_t) -> c_int { let mut state: mbstate_t = mbstate_t {}; let result: usize = unsafe { wcrtomb(s, wc, &raw mut state) }; if result == -1isize as usize { return -1; } if result == -2isize as usize { return -1; } result as c_int }