Skip to main content

relibc/header/stdlib/
mod.rs

1//! `stdlib.h` implementation.
2//!
3//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stdlib.h.html>.
4
5use core::{convert::TryFrom, intrinsics, iter, mem, ptr, slice};
6use rand::{
7    RngExt, SeedableRng,
8    distr::{Alphanumeric, Distribution, Uniform},
9};
10use rand_jitter::JitterRng;
11use rand_xorshift::XorShiftRng;
12
13use crate::{
14    byte_literal::ByteLiteral,
15    c_str::CStr,
16    error::{Errno, ResultExt},
17    fs::File,
18    header::{
19        ctype,
20        errno::{self, *},
21        fcntl::*,
22        limits,
23        stdio::flush_io_streams,
24        stdlib::sort::{QsortContext, QsortRContext},
25        string::*,
26        sys_ioctl::*,
27        time::constants::CLOCK_MONOTONIC,
28        unistd::{self, _SC_PAGESIZE, sysconf},
29        wchar::*,
30    },
31    ld_so,
32    out::Out,
33    platform::{
34        self, Pal, Sys,
35        types::{
36            c_char, c_double, c_float, c_int, c_long, c_longlong, c_uint, c_ulong, c_ulonglong,
37            c_ushort, c_void, size_t, ssize_t, uintptr_t, wchar_t,
38        },
39    },
40    raw_cell::RawCell,
41    sync::Once,
42};
43
44mod rand48;
45mod random;
46mod sort;
47
48/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stdlib.h.html>.
49pub const EXIT_FAILURE: c_int = 1;
50/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stdlib.h.html>.
51pub const EXIT_SUCCESS: c_int = 0;
52/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stdlib.h.html>.
53pub const RAND_MAX: c_int = 2_147_483_647;
54
55/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stdlib.h.html>.
56//Maximum number of bytes in a multibyte character for the current locale
57pub const MB_CUR_MAX: c_int = 4;
58/// Actually specified for `limits.h`?
59//Maximum number of bytes in a multibyte characters for any locale
60pub const MB_LEN_MAX: c_int = 4;
61
62static ATEXIT_FUNCS: RawCell<[Option<extern "C" fn()>; 32]> = RawCell::new([None; 32]);
63static AT_QUICK_EXIT_FUNCS: RawCell<[Option<extern "C" fn()>; 32]> = RawCell::new([None; 32]);
64static L64A_BUFFER: RawCell<[c_char; 7]> = RawCell::new([0; 7]); // up to 6 digits plus null terminator
65static mut RNG: Option<XorShiftRng> = None;
66
67// TODO: This could be const fn, but the trait system won't allow that.
68static RNG_SAMPLER: Once<Uniform<c_int>> = Once::new();
69
70fn rng_sampler() -> &'static Uniform<c_int> {
71    RNG_SAMPLER.call_once(|| Uniform::new_inclusive(0, RAND_MAX).expect("within bounds"))
72}
73
74/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/_Exit.html>.
75#[unsafe(no_mangle)]
76pub extern "C" fn _Exit(status: c_int) -> ! {
77    unistd::_exit(status);
78}
79
80/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/a64l.html>.
81#[unsafe(no_mangle)]
82pub unsafe extern "C" fn a64l(s: *const c_char) -> c_long {
83    // Early return upon null pointer argument
84    if s.is_null() {
85        return 0;
86    }
87
88    // POSIX says only the low-order 32 bits are used.
89    let mut l: i32 = 0;
90
91    // Handle up to 6 input characters (excl. null terminator)
92    for i in 0..6 {
93        let digit_char = unsafe { *s.offset(i) };
94
95        let digit_value = match digit_char {
96            0 => break, // Null terminator encountered
97            46..=57 => {
98                // ./0123456789 represents values 0 to 11. b'.' == 46
99                digit_char - 46
100            }
101            65..=90 => {
102                // A-Z for values 12 to 37. b'A' == 65, 65-12 == 53
103                digit_char - 53
104            }
105            97..=122 => {
106                // a-z for values 38 to 63. b'a' == 97, 97-38 == 59
107                digit_char - 59
108            }
109            _ => return 0, // Early return for anything else
110        };
111
112        l |= i32::from(digit_value) << (6 * i);
113    }
114
115    c_long::from(l)
116}
117
118/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/abort.html>.
119#[unsafe(no_mangle)]
120pub unsafe extern "C" fn abort() -> ! {
121    log::error!("Abort");
122    intrinsics::abort();
123}
124
125#[cfg(not(target_pointer_width = "64"))]
126#[unsafe(no_mangle)]
127static __stack_chk_guard: uintptr_t = 0x19fcadfe;
128
129#[cfg(target_pointer_width = "64")]
130#[unsafe(no_mangle)]
131static __stack_chk_guard: uintptr_t = 0xd048c37519fcadfe;
132
133#[unsafe(no_mangle)]
134unsafe extern "C" fn __stack_chk_fail() -> ! {
135    unsafe { abort() };
136}
137
138/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/abs.html>.
139#[unsafe(no_mangle)]
140pub extern "C" fn abs(i: c_int) -> c_int {
141    i.abs()
142}
143
144/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aligned_alloc.html>.
145#[unsafe(no_mangle)]
146pub unsafe extern "C" fn aligned_alloc(alignment: size_t, size: size_t) -> *mut c_void {
147    if alignment == 0 || !size.is_multiple_of(alignment) {
148        platform::ERRNO.set(EINVAL);
149        return ptr::null_mut();
150    }
151    /* The size-is-multiple-of-alignment requirement is the only
152     * difference between aligned_alloc() and memalign(). */
153    #[allow(deprecated)]
154    unsafe {
155        memalign(alignment, size)
156    }
157}
158
159/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/at_quick_exit.html>.
160#[unsafe(no_mangle)]
161pub unsafe extern "C" fn at_quick_exit(func: Option<extern "C" fn()>) -> c_int {
162    for i in 0..unsafe { AT_QUICK_EXIT_FUNCS.unsafe_ref().len() } {
163        if unsafe { AT_QUICK_EXIT_FUNCS.unsafe_ref() }[i].is_none() {
164            (unsafe { AT_QUICK_EXIT_FUNCS.unsafe_mut() })[i] = func;
165            return 0;
166        }
167    }
168
169    1
170}
171
172/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/atexit.html>.
173#[unsafe(no_mangle)]
174pub unsafe extern "C" fn atexit(func: Option<extern "C" fn()>) -> c_int {
175    for i in 0..unsafe { ATEXIT_FUNCS.unsafe_ref().len() } {
176        if unsafe { ATEXIT_FUNCS.unsafe_ref() }[i].is_none() {
177            (unsafe { ATEXIT_FUNCS.unsafe_mut() })[i] = func;
178            return 0;
179        }
180    }
181
182    1
183}
184
185/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/atof.html>.
186#[unsafe(no_mangle)]
187pub unsafe extern "C" fn atof(s: *const c_char) -> c_double {
188    unsafe { strtod(s, ptr::null_mut()) }
189}
190
191macro_rules! dec_num_from_ascii {
192    ($s:expr, $t:ty) => {{
193        let mut s = $s;
194        // Iterate past whitespace
195        while ctype::isspace(c_int::from(unsafe { *s })) != 0 {
196            s = unsafe { s.offset(1) };
197        }
198
199        // Find out if there is a - sign
200        let neg_sign = match unsafe { *s } {
201            0x2d => {
202                s = unsafe { s.offset(1) };
203                true
204            }
205            // '+' increment s and continue parsing
206            0x2b => {
207                s = unsafe { s.offset(1) };
208                false
209            }
210            _ => false,
211        };
212
213        let mut n: $t = 0;
214        while ctype::isdigit(c_int::from(unsafe { *s })) != 0 {
215            n = 10 * n - (unsafe { *s } as $t - 0x30);
216            s = unsafe { s.offset(1) };
217        }
218
219        if neg_sign { n } else { -n }
220    }};
221}
222
223/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/atoi.html>.
224#[unsafe(no_mangle)]
225pub unsafe extern "C" fn atoi(s: *const c_char) -> c_int {
226    dec_num_from_ascii!(s, c_int)
227}
228
229/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/atol.html>.
230#[unsafe(no_mangle)]
231pub unsafe extern "C" fn atol(s: *const c_char) -> c_long {
232    dec_num_from_ascii!(s, c_long)
233}
234
235/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/atol.html>.
236#[unsafe(no_mangle)]
237pub unsafe extern "C" fn atoll(s: *const c_char) -> c_longlong {
238    dec_num_from_ascii!(s, c_longlong)
239}
240
241unsafe extern "C" fn void_cmp(a: *const c_void, b: *const c_void) -> c_int {
242    (unsafe { *(a.cast::<i32>()) }) - unsafe { *(b.cast::<i32>()) } as c_int
243}
244
245/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/bsearch.html>.
246#[unsafe(no_mangle)]
247pub unsafe extern "C" fn bsearch(
248    key: *const c_void,
249    base: *const c_void,
250    nel: size_t,
251    width: size_t,
252    compar: Option<unsafe extern "C" fn(*const c_void, *const c_void) -> c_int>,
253) -> *mut c_void {
254    let mut start = base;
255    let mut len = nel;
256    let cmp_fn = compar.unwrap_or(void_cmp);
257    while len > 0 {
258        let med = (start as size_t + (len >> 1) * width) as *const c_void;
259        let diff = unsafe { cmp_fn(key, med) };
260        if diff == 0 {
261            return med.cast_mut();
262        } else if diff > 0 {
263            start = (med as usize + width) as *const c_void;
264            len -= 1;
265        }
266        len >>= 1;
267    }
268    ptr::null_mut()
269}
270
271/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/calloc.html>.
272#[unsafe(no_mangle)]
273pub unsafe extern "C" fn calloc(nelem: size_t, elsize: size_t) -> *mut c_void {
274    //Handle possible integer overflow in size calculation
275    match nelem.checked_mul(elsize) {
276        Some(size) => {
277            /* If allocation fails here, errno setting will be handled
278             * by malloc() */
279            let ptr = unsafe { malloc(size) };
280            if !ptr.is_null() {
281                unsafe { ptr.write_bytes(0, size) };
282            }
283            ptr
284        }
285        None => {
286            // For overflowing multiplication, we have to set errno here
287            platform::ERRNO.set(ENOMEM);
288            ptr::null_mut()
289        }
290    }
291}
292
293/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/div.html>.
294#[repr(C)]
295pub struct div_t {
296    quot: c_int,
297    rem: c_int,
298}
299
300/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/div.html>.
301#[unsafe(no_mangle)]
302pub extern "C" fn div(numer: c_int, denom: c_int) -> div_t {
303    div_t {
304        quot: numer / denom,
305        rem: numer % denom,
306    }
307}
308
309/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/drand48.html>.
310///
311/// # Panics
312/// Panics if the function is unable to obtain a lock on the generator's global
313/// state.
314#[unsafe(no_mangle)]
315pub extern "C" fn drand48() -> c_double {
316    let params = rand48::params();
317    let mut xsubi = rand48::xsubi_lock();
318    *xsubi = params.step(*xsubi);
319    xsubi.get_f64()
320}
321
322/// See <https://pubs.opengroup.org/onlinepubs/009695399/functions/ecvt.html>.
323///
324/// # Deprecation
325/// The `ecvt()` function was marked as legacy in the Open Group Base
326/// Specifications Issue 6, and the function was removed in Issue 7.
327#[deprecated]
328// #[unsafe(no_mangle)]
329pub extern "C" fn ecvt(
330    value: c_double,
331    ndigit: c_int,
332    decpt: *mut c_int,
333    sign: *mut c_int,
334) -> *mut c_char {
335    unimplemented!();
336}
337
338/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/drand48.html>.
339///
340/// # Safety
341/// The caller must ensure that `xsubi` is convertible to a
342/// `&mut [c_ushort; 3]`.
343///
344/// # Panics
345/// Panics if the function is unable to obtain a lock on the generator's global
346/// state.
347#[unsafe(no_mangle)]
348pub unsafe extern "C" fn erand48(xsubi: *mut c_ushort) -> c_double {
349    let params = rand48::params();
350    let xsubi_mut: &mut [c_ushort; 3] =
351        unsafe { slice::from_raw_parts_mut(xsubi, 3).try_into().unwrap() };
352    let new_xsubi_value = params.step(xsubi_mut.into());
353    *xsubi_mut = new_xsubi_value.into();
354    new_xsubi_value.get_f64()
355}
356
357/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/exit.html>.
358#[unsafe(no_mangle)]
359pub unsafe extern "C" fn exit(status: c_int) -> ! {
360    unsafe extern "C" {
361        static __fini_array_start: extern "C" fn();
362        static __fini_array_end: extern "C" fn();
363    }
364
365    for i in (0..unsafe { ATEXIT_FUNCS.unsafe_ref().len() }).rev() {
366        if let Some(func) = unsafe { ATEXIT_FUNCS.unsafe_ref() }[i] {
367            (func)();
368        }
369    }
370
371    // Look for the neighbor functions in memory until the end
372    let mut f = core::ptr::from_ref(unsafe { &__fini_array_end });
373    #[allow(clippy::op_ref)]
374    while f > &raw const __fini_array_start {
375        f = unsafe { f.offset(-1) };
376        (unsafe { *f })();
377    }
378
379    unsafe { ld_so::fini() };
380
381    unsafe { crate::pthread::terminate_from_main_thread() };
382
383    unsafe { flush_io_streams() };
384
385    Sys::exit(status);
386}
387
388/// See <https://pubs.opengroup.org/onlinepubs/009695399/functions/ecvt.html>.
389///
390/// # Deprecation
391/// The `fcvt()` function was marked as legacy in the Open Group Base
392/// Specifications Issue 6, and the function was removed in Issue 7.
393#[deprecated]
394// #[unsafe(no_mangle)]
395pub extern "C" fn fcvt(
396    value: c_double,
397    ndigit: c_int,
398    decpt: *mut c_int,
399    sign: *mut c_int,
400) -> *mut c_char {
401    unimplemented!();
402}
403
404/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/free.html>.
405#[unsafe(no_mangle)]
406pub unsafe extern "C" fn free(ptr: *mut c_void) {
407    unsafe { platform::free(ptr) };
408}
409
410/// See <https://pubs.opengroup.org/onlinepubs/009695399/functions/ecvt.html>.
411///
412/// # Deprecation
413/// The `gcvt()` function was marked as legacy in the Open Group Base
414/// Specifications Issue 6, and the function was removed in Issue 7.
415#[deprecated]
416// #[unsafe(no_mangle)]
417pub extern "C" fn gcvt(value: c_double, ndigit: c_int, buf: *mut c_char) -> *mut c_char {
418    unimplemented!();
419}
420
421unsafe fn find_env(search: *const c_char) -> Option<(usize, *mut c_char)> {
422    for (i, mut item) in platform::environ_iter().enumerate() {
423        let mut search = search;
424        loop {
425            let end_of_query =
426                unsafe { *search } == 0 || unsafe { *search } == ByteLiteral::cast_cchar(b'=');
427            if unsafe { *item } == 0 {
428                //TODO: environ has an item without value, is this a problem?
429                break;
430            }
431            if unsafe { *item } == ByteLiteral::cast_cchar(b'=') || end_of_query {
432                if unsafe { *item } == ByteLiteral::cast_cchar(b'=') && end_of_query {
433                    // Both keys env here
434                    return Some((i, unsafe { item.add(1) }));
435                } else {
436                    break;
437                }
438            }
439
440            if unsafe { *item } != unsafe { *search } {
441                break;
442            }
443
444            item = unsafe { item.add(1) };
445            search = unsafe { search.add(1) };
446        }
447    }
448
449    None
450}
451
452/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getenv.html>.
453#[unsafe(no_mangle)]
454pub unsafe extern "C" fn getenv(name: *const c_char) -> *mut c_char {
455    unsafe { find_env(name) }
456        .map(|val| val.1)
457        .unwrap_or(ptr::null_mut())
458}
459
460/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getsubopt.html>.
461#[unsafe(no_mangle)]
462pub unsafe extern "C" fn getsubopt(
463    optionp: *mut *mut c_char,
464    keylistp: *const *mut c_char,
465    valuep: *mut *mut c_char,
466) -> c_int {
467    if optionp.is_null()
468        || (unsafe { *optionp }).is_null()
469        || keylistp.is_null()
470        || valuep.is_null()
471    {
472        return -1;
473    }
474
475    let start = unsafe { *optionp };
476    let mut cursor = start;
477    let mut found_comma = false;
478
479    while unsafe { *cursor } != 0 {
480        if unsafe { *cursor } == ByteLiteral::cast_cchar(b',') {
481            unsafe { *cursor = 0 };
482            unsafe { *optionp = cursor.add(1) };
483            found_comma = true;
484            break;
485        }
486        cursor = unsafe { cursor.add(1) };
487    }
488
489    if !found_comma {
490        unsafe { *optionp = cursor };
491    }
492
493    let mut i = 0;
494    while !(unsafe { *keylistp.add(i) }).is_null() {
495        let token = unsafe { *keylistp.add(i) };
496        let token_len = unsafe { strlen(token) };
497
498        if unsafe { strncmp(start, token, token_len) } == 0 {
499            let suffix_char = unsafe { *start.add(token_len) };
500
501            if suffix_char == ByteLiteral::cast_cchar(b'=') {
502                unsafe { *valuep = start.add(token_len + 1) };
503                return i as c_int;
504            } else if suffix_char == 0 {
505                unsafe { *valuep = ptr::null_mut() };
506                return i as c_int;
507            }
508        }
509        i += 1;
510    }
511
512    unsafe { *valuep = start };
513    -1
514}
515
516/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/grantpt.html>.
517#[unsafe(no_mangle)]
518pub extern "C" fn grantpt(fildes: c_int) -> c_int {
519    // No-op on Linux and Redox
520    0
521}
522
523/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/initstate.html>.
524// Ported from musl
525#[unsafe(no_mangle)]
526pub unsafe extern "C" fn initstate(seed: c_uint, state: *mut c_char, size: size_t) -> *mut c_char {
527    if size < 8 {
528        ptr::null_mut()
529    } else {
530        let mut random_state = random::state_lock();
531        let old_state = unsafe { random_state.save() };
532        random_state.n = match size {
533            0..=7 => unreachable!(), // ensured above
534            8..=31 => 0,
535            32..=63 => 7,
536            64..=127 => 15,
537            128..=255 => 31,
538            _ => 63,
539        };
540
541        random_state.x_ptr = unsafe { (state.cast::<[u8; 4]>()).offset(1) };
542        unsafe { random_state.seed(seed) };
543        unsafe { random_state.save() };
544
545        old_state.cast::<_>()
546    }
547}
548
549/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/drand48.html>.
550///
551/// # Safety
552/// The caller must ensure that `xsubi` is convertible to a
553/// `&mut [c_ushort; 3]`.
554///
555/// # Panics
556/// Panics if the function is unable to obtain a lock on the generator's global
557/// state.
558#[unsafe(no_mangle)]
559pub unsafe extern "C" fn jrand48(xsubi: *mut c_ushort) -> c_long {
560    let params = rand48::params();
561    let xsubi_mut: &mut [c_ushort; 3] = unsafe { slice::from_raw_parts_mut(xsubi, 3) }
562        .try_into()
563        .unwrap();
564    let new_xsubi_value = params.step(xsubi_mut.into());
565    *xsubi_mut = new_xsubi_value.into();
566    new_xsubi_value.get_i32()
567}
568
569/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/a64l.html>.
570#[unsafe(no_mangle)]
571pub unsafe extern "C" fn l64a(value: c_long) -> *mut c_char {
572    // POSIX says we should only consider the lower 32 bits of value.
573    let value_as_i32 = value as i32;
574
575    /* If we pretend to extend the 32-bit value with 4 binary zeros, we
576     * would get a 36-bit integer. The number of base-64 digits to be
577     * left unused can then be found by taking the number of leading
578     * zeros, dividing by 6 and rounding down (i.e. using integer
579     * division). */
580    let num_output_digits = usize::try_from(6 - (value_as_i32.leading_zeros() + 4) / 6).unwrap();
581
582    // Reset buffer (and have null terminator in place for any result)
583    unsafe { L64A_BUFFER.unsafe_set([0; 7]) };
584
585    for i in 0..num_output_digits {
586        // Conversion to c_char always succeeds for the range 0..=63
587        let digit_value = c_char::try_from((value_as_i32 >> (6 * i)) & 63).unwrap();
588
589        (unsafe { L64A_BUFFER.unsafe_mut() })[i] = match digit_value {
590            0..=11 => {
591                // ./0123456789 for values 0 to 11. b'.' == 46
592                46 + digit_value
593            }
594            12..=37 => {
595                // A-Z for values 12 to 37. b'A' == 65, 65-12 == 53
596                53 + digit_value
597            }
598            38..=63 => {
599                // a-z for values 38 to 63. b'a' == 97, 97-38 == 59
600                59 + digit_value
601            }
602            _ => unreachable!(), // Guaranteed by taking "& 63" above
603        };
604    }
605
606    unsafe { L64A_BUFFER.unsafe_mut().as_mut_ptr() }
607}
608
609/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/labs.html>.
610#[unsafe(no_mangle)]
611pub extern "C" fn labs(i: c_long) -> c_long {
612    i.abs()
613}
614
615/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/drand48.html>.
616///
617/// # Safety
618/// The caller must ensure that `param` is convertible to a
619/// `&mut [c_ushort; 7]`.
620///
621/// # Panics
622/// Panics if the function is unable to obtain a lock on the generator's global
623/// state.
624#[unsafe(no_mangle)]
625pub unsafe extern "C" fn lcong48(param: *mut c_ushort) {
626    let mut xsubi = rand48::xsubi_lock();
627    let mut params = rand48::params_mut();
628
629    let param_slice = unsafe { slice::from_raw_parts(param, 7) };
630
631    let xsubi_ref: &[c_ushort; 3] = param_slice[0..3].try_into().unwrap();
632    let a_ref: &[c_ushort; 3] = param_slice[3..6].try_into().unwrap();
633    let c = param_slice[6];
634
635    *xsubi = xsubi_ref.into();
636    params.set(a_ref, c);
637}
638
639/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ldiv.html>.
640#[repr(C)]
641pub struct ldiv_t {
642    quot: c_long,
643    rem: c_long,
644}
645
646/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ldiv.html>.
647#[unsafe(no_mangle)]
648pub extern "C" fn ldiv(numer: c_long, denom: c_long) -> ldiv_t {
649    ldiv_t {
650        quot: numer / denom,
651        rem: numer % denom,
652    }
653}
654
655/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/labs.html>.
656#[unsafe(no_mangle)]
657pub extern "C" fn llabs(i: c_longlong) -> c_longlong {
658    i.abs()
659}
660
661/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ldiv.html>.
662#[repr(C)]
663pub struct lldiv_t {
664    quot: c_longlong,
665    rem: c_longlong,
666}
667
668/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ldiv.html>.
669#[unsafe(no_mangle)]
670pub extern "C" fn lldiv(numer: c_longlong, denom: c_longlong) -> lldiv_t {
671    lldiv_t {
672        quot: numer / denom,
673        rem: numer % denom,
674    }
675}
676
677/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/drand48.html>.
678///
679/// # Panics
680/// Panics if the function is unable to obtain a lock on the generator's global
681/// state.
682#[unsafe(no_mangle)]
683pub extern "C" fn lrand48() -> c_long {
684    let params = rand48::params();
685    let mut xsubi = rand48::xsubi_lock();
686    *xsubi = params.step(*xsubi);
687    xsubi.get_u31()
688}
689
690/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/malloc.html>.
691#[unsafe(no_mangle)]
692pub unsafe extern "C" fn malloc(size: size_t) -> *mut c_void {
693    let ptr = unsafe { platform::alloc(size) };
694    if ptr.is_null() {
695        platform::ERRNO.set(ENOMEM);
696    }
697    ptr
698}
699
700/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/posix_memalign.3.html>.
701#[deprecated]
702#[unsafe(no_mangle)]
703pub unsafe extern "C" fn memalign(alignment: size_t, size: size_t) -> *mut c_void {
704    if alignment.is_power_of_two() {
705        let ptr = unsafe { platform::alloc_align(size, alignment) };
706        if ptr.is_null() {
707            platform::ERRNO.set(ENOMEM);
708        }
709        ptr
710    } else {
711        platform::ERRNO.set(EINVAL);
712        ptr::null_mut()
713    }
714}
715
716/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/mblen.html>.
717#[unsafe(no_mangle)]
718pub unsafe extern "C" fn mblen(s: *const c_char, n: size_t) -> c_int {
719    let mut wc: wchar_t = 0;
720    let mut state: mbstate_t = mbstate_t {};
721    let result: usize = unsafe { mbrtowc(&raw mut wc, s, n, &raw mut state) };
722
723    if result == -1isize as usize {
724        return -1;
725    }
726    if result == -2isize as usize {
727        return -1;
728    }
729
730    result as i32
731}
732
733/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/mbstowcs.html>.
734#[unsafe(no_mangle)]
735pub unsafe extern "C" fn mbstowcs(pwcs: *mut wchar_t, mut s: *const c_char, n: size_t) -> size_t {
736    let mut state: mbstate_t = mbstate_t {};
737    unsafe { mbsrtowcs(pwcs, &raw mut s, n, &raw mut state) }
738}
739
740/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/mbtowc.html>.
741#[unsafe(no_mangle)]
742pub unsafe extern "C" fn mbtowc(pwc: *mut wchar_t, s: *const c_char, n: size_t) -> c_int {
743    let mut state: mbstate_t = mbstate_t {};
744    (unsafe { mbrtowc(pwc, s, n, &raw mut state) }) as c_int
745}
746
747fn inner_mktemp<T, F>(name: *mut c_char, suffix_len: c_int, mut attempt: F) -> Option<T>
748where
749    F: FnMut() -> Option<T>,
750{
751    let len = unsafe { strlen(name) as c_int };
752
753    if len < 6 || suffix_len > len - 6 {
754        platform::ERRNO.set(errno::EINVAL);
755        return None;
756    }
757
758    for i in (len - suffix_len - 6)..(len - suffix_len) {
759        if unsafe { *name.offset(i as isize) } != ByteLiteral::cast_cchar(b'X') {
760            platform::ERRNO.set(errno::EINVAL);
761            return None;
762        }
763    }
764
765    let mut rng = JitterRng::new_with_timer(get_nstime);
766    let _ = rng.test_timer();
767
768    for _ in 0..100 {
769        let char_iter = iter::repeat(())
770            .map(|()| rng.sample(Alphanumeric))
771            .take(6)
772            .enumerate();
773        unsafe {
774            for (i, c) in char_iter {
775                *name.offset((len as isize) - (suffix_len as isize) - (i as isize) - 1) =
776                    c as c_char
777            }
778        }
779
780        if let result @ Some(_) = attempt() {
781            return result;
782        }
783    }
784
785    platform::ERRNO.set(errno::EEXIST);
786
787    None
788}
789
790fn get_nstime() -> u64 {
791    unsafe {
792        let mut ts = mem::MaybeUninit::uninit();
793        if Sys::clock_gettime(CLOCK_MONOTONIC, Out::from_uninit_mut(&mut ts)).is_ok() {}; // TODO what to do if Err?
794        ts.assume_init().tv_nsec as u64
795    }
796}
797
798/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkdtemp.html>.
799#[unsafe(no_mangle)]
800pub unsafe extern "C" fn mkdtemp(name: *mut c_char) -> *mut c_char {
801    inner_mktemp(name, 0, || {
802        let name_c = unsafe { CStr::from_ptr(name) };
803        match Sys::mkdir(name_c, 0o700) {
804            Ok(()) => Some(name),
805            Err(_) => None,
806        }
807    })
808    .unwrap_or(ptr::null_mut())
809}
810
811/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkdtemp.html>.
812#[unsafe(no_mangle)]
813pub unsafe extern "C" fn mkostemp(name: *mut c_char, flags: c_int) -> c_int {
814    unsafe { mkostemps(name, 0, flags) }
815}
816
817/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/mkstemp.3.html>.
818#[unsafe(no_mangle)]
819pub unsafe extern "C" fn mkostemps(
820    name: *mut c_char,
821    suffix_len: c_int,
822    mut flags: c_int,
823) -> c_int {
824    // TODO: Rustify impl
825
826    flags &= !O_ACCMODE;
827    flags |= O_RDWR | O_CREAT | O_EXCL;
828
829    inner_mktemp(name, suffix_len, || {
830        let name = unsafe { CStr::from_ptr(name) };
831        let fd = Sys::open(name, flags, 0o600).or_minus_one_errno();
832
833        if fd >= 0 { Some(fd) } else { None }
834    })
835    .unwrap_or(-1)
836}
837
838/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkdtemp.html>.
839#[unsafe(no_mangle)]
840pub unsafe extern "C" fn mkstemp(name: *mut c_char) -> c_int {
841    unsafe { mkostemps(name, 0, 0) }
842}
843
844/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/mkstemp.3.html>.
845#[unsafe(no_mangle)]
846pub unsafe extern "C" fn mkstemps(name: *mut c_char, suffix_len: c_int) -> c_int {
847    unsafe { mkostemps(name, suffix_len, 0) }
848}
849
850/// See <https://pubs.opengroup.org/onlinepubs/009695399/functions/mktemp.html>.
851///
852/// # Deprecation
853/// The `mktemp()` function was marked as legacy in the Open Group Base
854/// Specifications Issue 6, and the function was removed in Issue 7.
855#[deprecated]
856#[unsafe(no_mangle)]
857pub unsafe extern "C" fn mktemp(name: *mut c_char) -> *mut c_char {
858    if inner_mktemp(name, 0, || {
859        let name = unsafe { CStr::from_ptr(name) };
860        if Sys::access(name, 0) == Err(Errno(ENOENT)) {
861            Some(())
862        } else {
863            None
864        }
865    })
866    .is_none()
867    {
868        unsafe { *name = 0 };
869    }
870    name
871}
872
873/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/drand48.html>.
874///
875/// # Panics
876/// Panics if the function is unable to obtain a lock on the generator's global
877/// state.
878#[unsafe(no_mangle)]
879pub extern "C" fn mrand48() -> c_long {
880    let params = rand48::params();
881    let mut xsubi = rand48::xsubi_lock();
882    *xsubi = params.step(*xsubi);
883    xsubi.get_i32()
884}
885
886/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/drand48.html>.
887///
888/// # Safety
889/// The caller must ensure that `xsubi` is convertible to a
890/// `&mut [c_ushort; 3]`.
891///
892/// # Panics
893/// Panics if the function is unable to obtain a lock on the generator's global
894/// state.
895#[unsafe(no_mangle)]
896pub unsafe extern "C" fn nrand48(xsubi: *mut c_ushort) -> c_long {
897    let params = rand48::params();
898    let xsubi_mut: &mut [c_ushort; 3] = unsafe { slice::from_raw_parts_mut(xsubi, 3) }
899        .try_into()
900        .unwrap();
901    let new_xsubi_value = params.step(xsubi_mut.into());
902    *xsubi_mut = new_xsubi_value.into();
903    new_xsubi_value.get_u31()
904}
905
906/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_memalign.html>.
907#[unsafe(no_mangle)]
908pub unsafe extern "C" fn posix_memalign(
909    memptr: *mut *mut c_void,
910    alignment: size_t,
911    size: size_t,
912) -> c_int {
913    const VOID_PTR_SIZE: usize = mem::size_of::<*mut c_void>();
914
915    if alignment.is_multiple_of(VOID_PTR_SIZE) && alignment.is_power_of_two() {
916        let ptr = unsafe { platform::alloc_align(size, alignment) };
917        unsafe { *memptr = ptr };
918        if ptr.is_null() { ENOMEM } else { 0 }
919    } else {
920        unsafe { *memptr = ptr::null_mut() };
921        EINVAL
922    }
923}
924
925/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_openpt.html>.
926#[unsafe(no_mangle)]
927pub unsafe extern "C" fn posix_openpt(flags: c_int) -> c_int {
928    #[cfg(target_os = "redox")]
929    let r = unsafe { open(c"/scheme/pty/ptmx".as_ptr(), flags) };
930    #[cfg(target_os = "linux")]
931    let r = unsafe { open(c"/dev/ptmx".as_ptr(), flags) };
932
933    if r < 0 && platform::ERRNO.get() == ENOSPC {
934        platform::ERRNO.set(EAGAIN);
935    }
936
937    r
938}
939
940/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ptsname.html>.
941#[unsafe(no_mangle)]
942pub unsafe extern "C" fn ptsname(fd: c_int) -> *mut c_char {
943    const PTS_BUFFER_LEN: usize = limits::TTY_NAME_MAX as usize;
944    static mut PTS_BUFFER: [c_char; PTS_BUFFER_LEN] = [0; PTS_BUFFER_LEN];
945    let ret = unsafe { ptsname_r(fd, (&raw mut PTS_BUFFER).cast(), PTS_BUFFER_LEN) };
946    if ret != 0 {
947        platform::ERRNO.set(ret);
948        ptr::null_mut()
949    } else {
950        (&raw mut PTS_BUFFER).cast()
951    }
952}
953
954/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ptsname.html>.
955#[unsafe(no_mangle)]
956pub unsafe extern "C" fn ptsname_r(fd: c_int, buf: *mut c_char, buflen: size_t) -> c_int {
957    if buf.is_null() {
958        platform::ERRNO.set(EINVAL);
959        EINVAL
960    } else {
961        unsafe { __ptsname_r(fd, buf, buflen) }
962    }
963}
964
965// ptsname_r is not allowed to set errno, but it has it as a return value.
966#[inline(always)]
967unsafe fn __ptsname_r(fd: c_int, buf: *mut c_char, buflen: size_t) -> c_int {
968    let mut pty: c_int = 0;
969
970    if unsafe { ioctl(fd, TIOCGPTN, ptr::from_mut(&mut pty).cast::<c_void>()) } == 0 {
971        // Linux and Redox use different resource names for PTS's.
972        #[cfg(target_os = "linux")]
973        let name = format!("/dev/pts/{}", pty);
974        #[cfg(target_os = "redox")]
975        let name = format!("/scheme/pty/{}", pty);
976        let len = name.len();
977        // We need + 1 to account for the NUL terminator.
978        if len + 1 > buflen {
979            ERANGE
980        } else {
981            // we have checked the string will fit in the buffer
982            // so can use strcpy safely
983            let s = name.as_ptr().cast();
984            unsafe { ptr::copy_nonoverlapping(s, buf, len) };
985            // NUL-terminate the result.
986            unsafe { *(buf.add(len + 1)) = 0 };
987            0
988        }
989    } else {
990        platform::ERRNO.get()
991    }
992}
993
994unsafe fn put_new_env(insert: *mut c_char) {
995    // XXX: Another problem is that `environ` can be set to any pointer, which means there is a
996    // chance of a memory leak. But we can check if it was the same as before, like musl does.
997    if unsafe { platform::environ } == unsafe { platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr() } {
998        {
999            let our_environ = unsafe { &mut *platform::OUR_ENVIRON.as_mut_ptr() };
1000            *our_environ.last_mut().unwrap() = insert;
1001            our_environ.push(core::ptr::null_mut());
1002        }
1003        // Likely a no-op but is needed due to Stacked Borrows.
1004        unsafe { platform::environ = platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr() };
1005    } else {
1006        {
1007            let our_environ = unsafe { &mut *platform::OUR_ENVIRON.as_mut_ptr() };
1008            our_environ.clear();
1009            our_environ.extend(platform::environ_iter());
1010            our_environ.push(insert);
1011            our_environ.push(core::ptr::null_mut());
1012        }
1013        unsafe { platform::environ = platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr() };
1014    }
1015}
1016
1017/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/putenv.html>.
1018#[unsafe(no_mangle)]
1019pub unsafe extern "C" fn putenv(insert: *mut c_char) -> c_int {
1020    assert_ne!(insert, ptr::null_mut(), "putenv(NULL)");
1021    if let Some((i, _)) = unsafe { find_env(insert) } {
1022        // XXX: The POSIX manual states that environment variables can be *set* via the `environ`
1023        // global variable. While we can check if a pointer belongs to our allocator, or check
1024        // `environ` against a vector which we control, it is likely not worth the effort.
1025        unsafe { platform::environ.add(i).write(insert) };
1026    } else {
1027        unsafe { put_new_env(insert) };
1028    }
1029    0
1030}
1031
1032/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/qsort.html>.
1033#[unsafe(no_mangle)]
1034pub unsafe extern "C" fn qsort(
1035    base: *mut c_void,
1036    nel: size_t,
1037    width: size_t,
1038    compar: Option<extern "C" fn(*const c_void, *const c_void) -> c_int>,
1039) {
1040    if let Some(comp) = compar {
1041        // XXX: check width too?  not specified
1042        if nel > 0 {
1043            // XXX: maybe try to do mergesort/timsort first and fallback to introsort if memory
1044            //      allocation fails?  not sure what is ideal
1045            let mut ctx = QsortContext { comp };
1046            unsafe { sort::introsort(base.cast::<c_char>(), nel, width, &mut ctx) };
1047        }
1048    }
1049}
1050
1051/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/qsort_r.html>.
1052#[unsafe(no_mangle)]
1053pub unsafe extern "C" fn qsort_r(
1054    base: *mut c_void,
1055    nel: size_t,
1056    width: size_t,
1057    compar: Option<extern "C" fn(*const c_void, *const c_void, *mut c_void) -> c_int>,
1058    arg: *mut c_void,
1059) {
1060    if let (Some(comp), true) = (compar, nel > 0) {
1061        let mut ctx = QsortRContext { comp, arg };
1062        unsafe { sort::introsort(base.cast::<c_char>(), nel, width, &mut ctx) };
1063    }
1064}
1065
1066/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/quick_exit.html>.
1067#[unsafe(no_mangle)]
1068pub unsafe extern "C" fn quick_exit(status: c_int) -> ! {
1069    for i in (0..unsafe { AT_QUICK_EXIT_FUNCS.unsafe_ref() }.len()).rev() {
1070        if let Some(func) = unsafe { AT_QUICK_EXIT_FUNCS.unsafe_ref() }[i] {
1071            (func)();
1072        }
1073    }
1074
1075    Sys::exit(status);
1076}
1077
1078/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/rand.html>.
1079#[unsafe(no_mangle)]
1080pub unsafe extern "C" fn rand() -> c_int {
1081    unsafe {
1082        match RNG {
1083            Some(ref mut rng) => rng_sampler().sample(rng),
1084            None => {
1085                let mut rng = XorShiftRng::from_seed([1; 16]);
1086                let ret = rng_sampler().sample(&mut rng);
1087                RNG = Some(rng);
1088                ret
1089            }
1090        }
1091    }
1092}
1093
1094/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/rand.html>.
1095///
1096/// # Deprecation
1097/// The `rand_r()` function was marked as obsolescent in the Open Group Base
1098/// Specifications Issue 7, and the function was removed in Issue 8.
1099#[deprecated]
1100#[unsafe(no_mangle)]
1101pub unsafe extern "C" fn rand_r(seed: *mut c_uint) -> c_int {
1102    if seed.is_null() {
1103        errno::EINVAL
1104    } else {
1105        // set the type explicitly so this will fail if the array size for XorShiftRng changes
1106        let seed_arr: [u8; 16] = unsafe { mem::transmute([*seed; 16 / mem::size_of::<c_uint>()]) };
1107
1108        let mut rng = XorShiftRng::from_seed(seed_arr);
1109        let ret = rng_sampler().sample(&mut rng);
1110
1111        unsafe { *seed = ret as _ };
1112
1113        ret
1114    }
1115}
1116
1117/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/initstate.html>.
1118// Ported from musl
1119#[unsafe(no_mangle)]
1120pub unsafe extern "C" fn random() -> c_long {
1121    let mut random_state = random::state_lock();
1122
1123    let k: u32;
1124
1125    unsafe { random_state.ensure_x_ptr_init() };
1126
1127    if random_state.n == 0 {
1128        let x_old = u32::from_ne_bytes(unsafe { *random_state.x_ptr });
1129        let x_new = random::lcg31_step(x_old);
1130        unsafe { *random_state.x_ptr = x_new.to_ne_bytes() };
1131        k = x_new;
1132    } else {
1133        // The non-u32-aligned way of saying x[i] += x[j]...
1134        let x_i_old =
1135            u32::from_ne_bytes(unsafe { *random_state.x_ptr.add(usize::from(random_state.i)) });
1136        let x_j =
1137            u32::from_ne_bytes(unsafe { *random_state.x_ptr.add(usize::from(random_state.j)) });
1138        let x_i_new = x_i_old.wrapping_add(x_j);
1139        unsafe { *random_state.x_ptr.add(usize::from(random_state.i)) = x_i_new.to_ne_bytes() };
1140
1141        k = x_i_new >> 1;
1142
1143        random_state.i += 1;
1144        if random_state.i == random_state.n {
1145            random_state.i = 0;
1146        }
1147
1148        random_state.j += 1;
1149        if random_state.j == random_state.n {
1150            random_state.j = 0;
1151        }
1152    }
1153
1154    /* Both branches of this function result in a "u31", which will
1155     * always fit in a c_long. */
1156    #[cfg(not(target_arch = "x86"))]
1157    return c_long::from(k);
1158    #[cfg(target_arch = "x86")] // c_long on x86 is i32 so not infallible cast
1159    c_long::try_from(k).unwrap()
1160}
1161
1162/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/realloc.html>.
1163#[unsafe(no_mangle)]
1164pub unsafe extern "C" fn realloc(ptr: *mut c_void, size: size_t) -> *mut c_void {
1165    let new_ptr = unsafe { platform::realloc(ptr, size) };
1166    if new_ptr.is_null() {
1167        platform::ERRNO.set(ENOMEM);
1168    }
1169    new_ptr
1170}
1171
1172/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/realloc.html>.
1173#[unsafe(no_mangle)]
1174pub unsafe extern "C" fn reallocarray(ptr: *mut c_void, m: size_t, n: size_t) -> *mut c_void {
1175    //Handle possible integer overflow in size calculation
1176    match m.checked_mul(n) {
1177        Some(size) => unsafe { realloc(ptr, size) },
1178        None => {
1179            // For overflowing multiplication, we have to set errno here
1180            platform::ERRNO.set(ENOMEM);
1181            ptr::null_mut()
1182        }
1183    }
1184}
1185
1186/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/realpath.html>.
1187#[unsafe(no_mangle)]
1188pub unsafe extern "C" fn realpath(pathname: *const c_char, resolved: *mut c_char) -> *mut c_char {
1189    let ptr = if resolved.is_null() {
1190        (unsafe { malloc(limits::PATH_MAX) }).cast::<c_char>()
1191    } else {
1192        resolved
1193    };
1194
1195    let out = unsafe { slice::from_raw_parts_mut(ptr.cast::<u8>(), limits::PATH_MAX) };
1196    {
1197        let file = match File::open(unsafe { CStr::from_ptr(pathname) }, O_PATH | O_CLOEXEC) {
1198            Ok(file) => file,
1199            Err(_) => return ptr::null_mut(),
1200        };
1201
1202        let len = out.len();
1203        // TODO: better error handling
1204        let read = Sys::fpath(*file, &mut out[..len - 1])
1205            .map(|read| read as ssize_t)
1206            .or_minus_one_errno();
1207        if read < 0 {
1208            return ptr::null_mut();
1209        }
1210        out[read as usize] = 0;
1211    }
1212
1213    ptr
1214}
1215
1216/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getenv.html>.
1217// #[unsafe(no_mangle)]
1218pub unsafe extern "C" fn secure_getenv(name: *const c_char) -> *mut c_char {
1219    unimplemented!();
1220}
1221
1222/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/drand48.html>.
1223///
1224/// # Safety
1225/// The caller must ensure that `seed16v` is convertible to a `&[c_ushort; 3]`.
1226/// Additionally, the caller must ensure that the function has exclusive access
1227/// to the static buffer it returns; this includes avoiding simultaneous calls
1228/// to this function.
1229///
1230/// # Panics
1231/// Panics if the function is unable to obtain a lock on the generator's global
1232/// state.
1233#[unsafe(no_mangle)]
1234pub unsafe extern "C" fn seed48(seed16v: *mut c_ushort) -> *mut c_ushort {
1235    static RETURN_BUFFER: RawCell<[c_ushort; 3]> = RawCell::new([0; 3]);
1236
1237    let mut params = rand48::params_mut();
1238    let mut xsubi = rand48::xsubi_lock();
1239
1240    // SAFETY: the caller is required to ensure seed16v is convertible to
1241    // &[c_ushort; 3].
1242    let seed16v_ref: &[c_ushort; 3] = unsafe { slice::from_raw_parts(seed16v, 3) }
1243        .try_into()
1244        .unwrap();
1245
1246    // SAFETY: the caller is required to ensure exclusive access to
1247    // RETURN_BUFFER.
1248    let return_buffer_mut = unsafe { RETURN_BUFFER.unsafe_mut() };
1249    *return_buffer_mut = (*xsubi).into();
1250    *xsubi = seed16v_ref.into();
1251    params.reset();
1252    RETURN_BUFFER.as_mut_ptr().cast()
1253}
1254
1255unsafe fn copy_kv(
1256    existing: *mut c_char,
1257    key: *const c_char,
1258    value: *const c_char,
1259    key_len: usize,
1260    value_len: usize,
1261) {
1262    unsafe { core::ptr::copy_nonoverlapping(key, existing, key_len) };
1263    unsafe { core::ptr::write(existing.add(key_len), ByteLiteral::cast_cchar(b'=')) };
1264    unsafe { core::ptr::copy_nonoverlapping(value, existing.add(key_len + 1), value_len) };
1265    unsafe { core::ptr::write(existing.add(key_len + 1 + value_len), 0) };
1266}
1267
1268/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setenv.html>.
1269#[unsafe(no_mangle)]
1270pub unsafe extern "C" fn setenv(
1271    key: *const c_char,
1272    value: *const c_char,
1273    overwrite: c_int,
1274) -> c_int {
1275    let key_len = unsafe { strlen(key) };
1276    let value_len = unsafe { strlen(value) };
1277
1278    if let Some((i, existing)) = unsafe { find_env(key) } {
1279        if overwrite == 0 {
1280            return 0;
1281        }
1282
1283        let existing_len = unsafe { strlen(existing) };
1284
1285        if existing_len >= value_len {
1286            // Reuse existing element's allocation
1287            unsafe { core::ptr::copy_nonoverlapping(value, existing, value_len) };
1288            //TODO: fill to end with zeroes
1289            unsafe { core::ptr::write(existing.add(value_len), 0) };
1290        } else {
1291            // Reuse platform::environ slot, but allocate a new pointer.
1292            let ptr = unsafe { platform::alloc(key_len as usize + 1 + value_len as usize + 1) }
1293                .cast::<c_char>();
1294            unsafe { copy_kv(ptr, key, value, key_len, value_len) };
1295            unsafe { platform::environ.add(i).write(ptr) };
1296        }
1297    } else {
1298        // Expand platform::environ and allocate a new pointer.
1299        let ptr = unsafe { platform::alloc(key_len as usize + 1 + value_len as usize + 1) }
1300            .cast::<c_char>();
1301        unsafe { copy_kv(ptr, key, value, key_len, value_len) };
1302        unsafe { put_new_env(ptr) };
1303    }
1304
1305    //platform::free(platform::inner_environ[index] as *mut c_void);
1306
1307    0
1308}
1309
1310/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setkey.html>.
1311///
1312/// # Deprecation
1313/// The `setkey()` function was marked as obsolescent in the Open Group Base
1314/// Specifications Issue 8.
1315#[deprecated]
1316// #[unsafe(no_mangle)]
1317pub unsafe extern "C" fn setkey(key: *const c_char) {
1318    unimplemented!();
1319}
1320
1321/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/initstate.html>.
1322// Ported from musl. The state parameter is no longer const in newer versions of POSIX.
1323#[unsafe(no_mangle)]
1324pub unsafe extern "C" fn setstate(state: *mut c_char) -> *mut c_char {
1325    let mut random_state = random::state_lock();
1326
1327    let old_state = unsafe { random_state.save() };
1328    unsafe { random_state.load(state.cast::<_>()) };
1329
1330    old_state.cast::<_>()
1331}
1332
1333/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/rand.html>.
1334#[unsafe(no_mangle)]
1335pub unsafe extern "C" fn srand(seed: c_uint) {
1336    unsafe { RNG = Some(XorShiftRng::from_seed([seed as u8; 16])) };
1337}
1338
1339/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/drand48.html>.
1340///
1341/// # Panics
1342/// Panics if the function is unable to obtain a lock on the generator's global
1343/// state.
1344#[unsafe(no_mangle)]
1345pub extern "C" fn srand48(seedval: c_long) {
1346    let mut params = rand48::params_mut();
1347    let mut xsubi = rand48::xsubi_lock();
1348
1349    params.reset();
1350    /* Set the high 32 bits of the 48-bit X_i value to the lower 32 bits
1351     * of the input argument, and the lower 16 bits to 0x330e, as
1352     * specified in POSIX. */
1353    *xsubi = ((u64::from(seedval as u32) << 16) | 0x330e)
1354        .try_into()
1355        .unwrap();
1356}
1357
1358/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/initstate.html>.
1359// Ported from musl
1360#[unsafe(no_mangle)]
1361pub unsafe extern "C" fn srandom(seed: c_uint) {
1362    let mut random_state = random::state_lock();
1363
1364    unsafe { random_state.seed(seed) };
1365}
1366
1367pub fn is_positive(ch: c_char) -> Option<(bool, isize)> {
1368    match ch {
1369        0 => None,
1370        ch if ch == ByteLiteral::cast_cchar(b'+') => Some((true, 1)),
1371        ch if ch == ByteLiteral::cast_cchar(b'-') => Some((false, 1)),
1372        _ => Some((true, 0)),
1373    }
1374}
1375
1376pub unsafe fn detect_base(s: *const c_char) -> Option<(c_int, isize)> {
1377    let first = unsafe { *s } as u8;
1378    match first {
1379        0 => None,
1380        b'0' => {
1381            let second = unsafe { *s.offset(1) } as u8;
1382            if second == b'X' || second == b'x' {
1383                Some((16, 2))
1384            } else if (b'0'..=b'7').contains(&second) {
1385                Some((8, 1))
1386            } else {
1387                // in this case, the prefix (0) is going to be the number
1388                Some((8, 0))
1389            }
1390        }
1391        _ => Some((10, 0)),
1392    }
1393}
1394
1395pub unsafe fn convert_octal(s: *const c_char) -> Option<(c_ulong, isize, bool)> {
1396    if unsafe { *s } != 0 && unsafe { *s } == ByteLiteral::cast_cchar(b'0') {
1397        if let Some((val, idx, overflow)) = unsafe { convert_integer(s.offset(1), 8) } {
1398            Some((val, idx + 1, overflow))
1399        } else {
1400            // in case the prefix is not actually a prefix
1401            Some((0, 1, false))
1402        }
1403    } else {
1404        None
1405    }
1406}
1407
1408pub unsafe fn convert_hex(s: *const c_char) -> Option<(c_ulong, isize, bool)> {
1409    if (unsafe { *s } != 0 && unsafe { *s } == ByteLiteral::cast_cchar(b'0'))
1410        && (unsafe { *s.offset(1) } != 0
1411            && (unsafe { *s.offset(1) } == ByteLiteral::cast_cchar(b'x')
1412                || unsafe { *s.offset(1) } == ByteLiteral::cast_cchar(b'X')))
1413    {
1414        unsafe { convert_integer(s.offset(2), 16) }
1415            .map(|(val, idx, overflow)| (val, idx + 2, overflow))
1416    } else {
1417        unsafe { convert_integer(s, 16) }
1418    }
1419}
1420
1421pub unsafe fn convert_integer(s: *const c_char, base: c_int) -> Option<(c_ulong, isize, bool)> {
1422    // -1 means the character is invalid
1423    #[rustfmt::skip]
1424    const LOOKUP_TABLE: [c_long; 256] = [
1425        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1426        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1427        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1428         0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,
1429        -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
1430        25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
1431        -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
1432        25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
1433        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1434        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1435        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1436        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1437        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1438        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1439        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1440        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1441    ];
1442
1443    let mut num: c_ulong = 0;
1444    let mut idx = 0;
1445    let mut overflowed = false;
1446
1447    loop {
1448        // `-1 as usize` is usize::MAX
1449        // `-1 as u8 as usize` is u8::MAX
1450        // It extends by the sign bit unless we cast it to unsigned first.
1451        let val = LOOKUP_TABLE[unsafe { *s.offset(idx) } as u8 as usize];
1452        if val == -1 || val as c_int >= base {
1453            break;
1454        } else {
1455            if let Some(res) = num
1456                .checked_mul(base as c_ulong)
1457                .and_then(|num| num.checked_add(val as c_ulong))
1458            {
1459                num = res;
1460            } else {
1461                platform::ERRNO.set(ERANGE);
1462                num = c_ulong::MAX;
1463                overflowed = true;
1464            }
1465
1466            idx += 1;
1467        }
1468    }
1469
1470    if idx > 0 {
1471        Some((num, idx, overflowed))
1472    } else {
1473        None
1474    }
1475}
1476
1477/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strtod.html>.
1478#[expect(clippy::cast_lossless)] // not all users of `strto_float_impl!` are lossless
1479#[unsafe(no_mangle)]
1480pub unsafe extern "C" fn strtod(s: *const c_char, endptr: *mut *mut c_char) -> c_double {
1481    strto_float_impl!(c_double, s, endptr)
1482}
1483
1484/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strtod.html>.
1485#[unsafe(no_mangle)]
1486pub unsafe extern "C" fn strtof(s: *const c_char, endptr: *mut *mut c_char) -> c_float {
1487    strto_float_impl!(c_float, s, endptr)
1488}
1489
1490/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strtol.html>.
1491///
1492/// Converts the initial portion of the string pointed to by `nptr` to a type
1493/// `long`.
1494///
1495/// Upon success, returns the converted value. If no conversion could be
1496/// performed or the value of `base` is not supported, returns `0`.
1497#[unsafe(no_mangle)]
1498pub unsafe extern "C" fn strtol(
1499    nptr: *const c_char,
1500    endptr: *mut *mut c_char,
1501    base: c_int,
1502) -> c_long {
1503    strto_impl!(c_long, true, c_long::MAX, c_long::MIN, nptr, endptr, base)
1504}
1505
1506// TODO: strtold(), when long double is available
1507
1508/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strtol.html>.
1509///
1510/// Converts the initial portion of the string pointed to by `nptr` to a type
1511/// `long long`.
1512///
1513/// Upon success, returns the converted value. If no conversion could be
1514/// performed or the value of `base` is not supported, returns `0`.
1515#[unsafe(no_mangle)]
1516pub unsafe extern "C" fn strtoll(
1517    nptr: *const c_char,
1518    endptr: *mut *mut c_char,
1519    base: c_int,
1520) -> c_longlong {
1521    strto_impl!(
1522        c_longlong,
1523        true,
1524        c_longlong::MAX,
1525        c_longlong::MIN,
1526        nptr,
1527        endptr,
1528        base
1529    )
1530}
1531
1532/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strtoul.html>.
1533///
1534/// Converts the initial portion of the string pointed to by `str` to a type
1535/// `unsigned long`.
1536///
1537/// Upon success, returns the converted value. If no conversion could be
1538/// performed or the value of `base` is not supported, returns `0`.
1539#[unsafe(no_mangle)]
1540pub unsafe extern "C" fn strtoul(
1541    str: *const c_char,
1542    endptr: *mut *mut c_char,
1543    base: c_int,
1544) -> c_ulong {
1545    strto_impl!(
1546        c_ulong,
1547        false,
1548        c_ulong::MAX,
1549        c_ulong::MIN,
1550        str,
1551        endptr,
1552        base
1553    )
1554}
1555
1556/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strtoul.html>.
1557///
1558/// Converts the initial portion of the string pointed to by `str` to a type
1559/// `unsigned long long`.
1560///
1561/// Upon success, returns the converted value. If no conversion could be
1562/// performed or the value of `base` is not supported, returns `0`.
1563#[unsafe(no_mangle)]
1564pub unsafe extern "C" fn strtoull(
1565    str: *const c_char,
1566    endptr: *mut *mut c_char,
1567    base: c_int,
1568) -> c_ulonglong {
1569    strto_impl!(
1570        c_ulonglong,
1571        false,
1572        c_ulonglong::MAX,
1573        c_ulonglong::MIN,
1574        str,
1575        endptr,
1576        base
1577    )
1578}
1579
1580/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/system.html>.
1581#[unsafe(no_mangle)]
1582pub unsafe extern "C" fn system(command: *const c_char) -> c_int {
1583    // TODO: rusty error handling?
1584    //TODO: share code with popen
1585
1586    // handle shell detection on command == NULL
1587    if command.is_null() {
1588        let status = unsafe { system(c"exit 0".as_ptr().cast::<c_char>()) };
1589        if status == 0 {
1590            return 1;
1591        } else {
1592            return 0;
1593        }
1594    }
1595
1596    let child_pid = unsafe { unistd::fork() };
1597    if child_pid == 0 {
1598        let command_nonnull = command.cast::<c_char>();
1599
1600        let shell = c"/bin/sh".as_ptr();
1601
1602        let args = [c"sh".as_ptr(), c"-c".as_ptr(), command_nonnull, ptr::null()];
1603
1604        unsafe { unistd::execv(shell.cast::<c_char>(), args.as_ptr().cast::<*mut c_char>()) };
1605
1606        unsafe { exit(127) };
1607
1608        unreachable!();
1609    } else if child_pid > 0 {
1610        let mut wstatus = 0;
1611        if Sys::waitpid(child_pid, Some(Out::from_mut(&mut wstatus)), 0).or_minus_one_errno() == -1
1612        {
1613            return -1;
1614        }
1615
1616        wstatus
1617    } else {
1618        -1
1619    }
1620}
1621
1622/// See <https://pubs.opengroup.org/onlinepubs/7908799/xsh/ttyslot.html>.
1623///
1624/// # Deprecation
1625/// The `ttyslot()` function was marked as obsolescent in the Open Group Base
1626/// Specifications Issue 5, and the function was removed in Issue 6.
1627#[deprecated]
1628// #[unsafe(no_mangle)]
1629pub extern "C" fn ttyslot() -> c_int {
1630    unimplemented!();
1631}
1632
1633/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/unlockpt.html>.
1634#[unsafe(no_mangle)]
1635pub unsafe extern "C" fn unlockpt(fildes: c_int) -> c_int {
1636    let mut u: c_int = 0;
1637    unsafe {
1638        ioctl(
1639            fildes,
1640            TIOCSPTLCK,
1641            ptr::from_mut::<i32>(&mut u).cast::<c_void>(),
1642        )
1643    }
1644}
1645
1646/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/unsetenv.html>.
1647#[unsafe(no_mangle)]
1648pub unsafe extern "C" fn unsetenv(key: *const c_char) -> c_int {
1649    if let Some((i, _)) = unsafe { find_env(key) } {
1650        if unsafe { platform::environ }
1651            == unsafe { platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr() }
1652        {
1653            // No need to worry about updating the pointer, this does not
1654            // reallocate in any way. And the final null is already shifted back.
1655            {
1656                let our_environ = unsafe { &mut *platform::OUR_ENVIRON.as_mut_ptr() };
1657                our_environ.remove(i);
1658            }
1659
1660            // My UB paranoia.
1661            unsafe { platform::environ = platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr() };
1662        } else {
1663            {
1664                let our_environ = unsafe { &mut *platform::OUR_ENVIRON.as_mut_ptr() };
1665                our_environ.clear();
1666                our_environ.extend(
1667                    platform::environ_iter()
1668                        .enumerate()
1669                        .filter(|&(j, _)| j != i)
1670                        .map(|(_, v)| v),
1671                );
1672                our_environ.push(core::ptr::null_mut());
1673            }
1674            unsafe { platform::environ = platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr() };
1675        }
1676    }
1677    0
1678}
1679
1680/// See <https://pubs.opengroup.org/onlinepubs/7908799/xsh/valloc.html>.
1681///
1682/// # Deprecation
1683/// The `valloc()` function was marked as obsolescent in the Open Group Base
1684/// Specifications Issue 5, and the function was removed in Issue 6.
1685#[deprecated]
1686#[unsafe(no_mangle)]
1687pub unsafe extern "C" fn valloc(size: size_t) -> *mut c_void {
1688    /* sysconf(_SC_PAGESIZE) is a c_long and may in principle not
1689     * convert correctly to a size_t. */
1690    match size_t::try_from(unsafe { sysconf(_SC_PAGESIZE) }) {
1691        Ok(page_size) => {
1692            /* valloc() is not supposed to be able to set errno to
1693             * EINVAL, hence no call to memalign(). */
1694            let ptr = unsafe { platform::alloc_align(size, page_size) };
1695            if ptr.is_null() {
1696                platform::ERRNO.set(ENOMEM);
1697            }
1698            ptr
1699        }
1700        Err(_) => {
1701            // A corner case. No errno setting.
1702            ptr::null_mut()
1703        }
1704    }
1705}
1706
1707/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcstombs.html>.
1708#[unsafe(no_mangle)]
1709pub unsafe extern "C" fn wcstombs(s: *mut c_char, mut pwcs: *const wchar_t, n: size_t) -> size_t {
1710    let mut state: mbstate_t = mbstate_t {};
1711    unsafe { wcsrtombs(s, &raw mut pwcs, n, &raw mut state) }
1712}
1713
1714/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wctomb.html>.
1715#[unsafe(no_mangle)]
1716pub unsafe extern "C" fn wctomb(s: *mut c_char, wc: wchar_t) -> c_int {
1717    let mut state: mbstate_t = mbstate_t {};
1718    let result: usize = unsafe { wcrtomb(s, wc, &raw mut state) };
1719
1720    if result == -1isize as usize {
1721        return -1;
1722    }
1723    if result == -2isize as usize {
1724        return -1;
1725    }
1726
1727    result as c_int
1728}