Skip to main content

c/
lib.rs

1#![cfg_attr(not(test), no_std)]
2#![feature(c_variadic)]
3#![feature(linkage)]
4#![cfg_attr(target_arch = "aarch64", feature(f128))]
5#![cfg_attr(target_arch = "riscv64", feature(f128))]
6#![allow(dead_code, non_camel_case_types)]
7// A libc necessarily keeps mutable global state (stdio tables, passwd/group
8// buffers, resolver state).  References to it are deliberate and confined to
9// the C ABI entry points.
10#![allow(static_mut_refs)]
11// C `_Complex` values are passed as two floating-point registers on every
12// supported target; Rust tuples use the same SysV/AAPCS classification.
13#![allow(improper_ctypes_definitions)]
14
15use core::ffi::{c_char, c_int, c_long, c_longlong, c_uint, c_ulong, c_ulonglong, c_void, VaList};
16use core::ptr::null_mut;
17use core::sync::atomic::{AtomicI32, AtomicUsize, Ordering};
18
19macro_rules! ERRNO {
20    () => { *errno_ptr() };
21}
22
23include!("encoding_tables.rs");
24include!("math_helpers.rs");
25include!("math_bitmanip.rs");
26include!("math_sqrtfmod.rs");
27include!("math_trig.rs");
28include!("math_exp.rs");
29include!("math_erf.rs");
30include!("math_lgamma.rs");
31include!("math_tgamma.rs");
32include!("math_bessel.rs");
33include!("alloc.rs");
34include!("math_log.rs");
35include!("math_pow.rs");
36include!("math_hypot.rs");
37include!("math_hyperbolic.rs");
38include!("math_invtrig.rs");
39include!("math_lrint.rs");
40include!("math_long_double.rs");
41include!("string_fast.rs");
42include!("vdso.rs");
43
44// ============================================================
45// errno
46// ============================================================
47
48const EILSEQ: c_int = 84;
49const EINVAL: c_int = 22;
50const EFAULT: c_int = 14;
51const E2BIG: c_int = 7;
52
53const LC_CTYPE: c_int = 0;
54const LC_NUMERIC: c_int = 1;
55const LC_TIME: c_int = 2;
56const LC_COLLATE: c_int = 3;
57const LC_MONETARY: c_int = 4;
58const LC_MESSAGES: c_int = 5;
59const LC_ALL: c_int = 6;
60const ENOMEM: c_int = 12;
61const EINTR: c_int = 4;
62const EPERM: c_int = 1;
63const EAGAIN: c_int = 11;
64const EBUSY: c_int = 16;
65const EDEADLK: c_int = 35;
66const ETIMEDOUT: c_int = 110;
67const ECANCELED: c_int = 125;
68const EOVERFLOW: c_int = 75;
69const EOWNERDEAD: c_int = 130;
70const ENOTRECOVERABLE: c_int = 131;
71
72// Static executables have no dynamic loader to build a TCB, so they keep the
73// traditional process-global errno. Dynamic programs store it in the TCB, just
74// below the thread pointer. The offset is inside the 256-byte TCB that
75// libldso.so allocates for every thread (0x00 self pointer, 0x08 generation,
76// 0x28 stack canary).
77static mut ERRNO_FALLBACK: c_int = 0;
78static ERRNO_TLS_ACTIVE: AtomicUsize = AtomicUsize::new(0);
79const ERRNO_TCB_OFFSET: usize = 0x30;
80
81#[inline]
82unsafe fn errno_ptr() -> *mut c_int {
83    if ERRNO_TLS_ACTIVE.load(Ordering::Relaxed) == 0 {
84        return core::ptr::addr_of_mut!(ERRNO_FALLBACK);
85    }
86    #[cfg(target_arch = "x86_64")]
87    let tp: usize = {
88        let tp: usize;
89        core::arch::asm!("mov {}, fs:[0]", out(reg) tp, options(nostack, preserves_flags));
90        tp
91    };
92    #[cfg(target_arch = "aarch64")]
93    let tp: usize = {
94        let tp: usize;
95        core::arch::asm!("mrs {}, tpidr_el0", out(reg) tp, options(nostack, preserves_flags));
96        tp
97    };
98    #[cfg(target_arch = "riscv64")]
99    let tp: usize = {
100        let tp: usize;
101        core::arch::asm!("mv {}, tp", out(reg) tp, options(nostack, preserves_flags));
102        tp
103    };
104    #[cfg(target_arch = "x86_64")]
105    let tcb = tp;
106    #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
107    let tcb = tp - 256;
108    (tcb + ERRNO_TCB_OFFSET) as *mut c_int
109}
110
111/// Called once from __libc_start_main. The dynamic loader has already set up
112/// a TCB for dynamic programs; static programs leave %fs / TP at zero.
113unsafe fn errno_init() {
114    let tp = arch_get_tp();
115    #[cfg(target_arch = "x86_64")]
116    let tcb = tp;
117    #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
118    let tcb = tp.wrapping_sub(256);
119    if tp != 0 && core::ptr::read(tcb as *const usize) == tcb {
120        ERRNO_TLS_ACTIVE.store(1, Ordering::Relaxed);
121    }
122}
123
124#[cfg(target_arch = "x86_64")]
125unsafe fn arch_get_tp() -> usize {
126    let mut tp: usize = 0;
127    let _ = <Arch as Syscalls>::syscall2(158 /* ARCH_PRCTL */, 0x1003 /* ARCH_GET_FS */, &mut tp as *mut usize as i64);
128    tp
129}
130
131#[cfg(target_arch = "aarch64")]
132unsafe fn arch_get_tp() -> usize {
133    let tp: usize;
134    core::arch::asm!("mrs {}, tpidr_el0", out(reg) tp, options(nostack, preserves_flags));
135    tp
136}
137
138#[cfg(target_arch = "riscv64")]
139unsafe fn arch_get_tp() -> usize {
140    let tp: usize;
141    core::arch::asm!("mv {}, tp", out(reg) tp, options(nostack, preserves_flags));
142    tp
143}
144
145/// Normalize a raw syscall result the way musl's `__syscall_ret` does:
146/// turn `-errno` into `-1` and publish the error through `errno`.
147#[inline]
148unsafe fn syscall_ret(r: i64) -> i64 {
149    if r < 0 {
150        *errno_ptr() = (-r) as c_int;
151        -1
152    } else {
153        r
154    }
155}
156
157#[no_mangle]
158pub unsafe extern "C" fn __errno_location() -> *mut c_int {
159    errno_ptr()
160}
161
162#[cfg(not(test))]
163#[panic_handler]
164fn panic(_: &core::panic::PanicInfo) -> ! {
165    loop {}
166}
167
168// Required by the Rust runtime; panic=abort never unwinds.
169#[cfg(not(test))]
170#[no_mangle]
171pub extern "C" fn rust_eh_personality() {}
172
173// ============================================================
174// Constants
175// ============================================================
176
177const PROT_READ: i32 = 1;
178const PROT_WRITE: i32 = 2;
179const MAP_SHARED: i32 = 0x01;
180const MAP_PRIVATE: i32 = 0x02;
181const MAP_ANONYMOUS: i32 = 0x20;
182
183// ============================================================
184// Architecture abstraction for syscalls
185// ============================================================
186
187trait Syscalls {
188    unsafe fn syscall0(n: i64) -> i64;
189    unsafe fn syscall1(n: i64, a1: i64) -> i64;
190    unsafe fn syscall2(n: i64, a1: i64, a2: i64) -> i64;
191    unsafe fn syscall3(n: i64, a1: i64, a2: i64, a3: i64) -> i64;
192    unsafe fn syscall4(n: i64, a1: i64, a2: i64, a3: i64, a4: i64) -> i64;
193    unsafe fn syscall5(n: i64, a1: i64, a2: i64, a3: i64, a4: i64, a5: i64) -> i64;
194    unsafe fn syscall6(n: i64, a1: i64, a2: i64, a3: i64, a4: i64, a5: i64, a6: i64) -> i64;
195    unsafe fn syscall_noreturn1(n: i64, a1: i64) -> !;
196}
197
198struct X86_64;
199struct Aarch64;
200struct Riscv64;
201
202#[cfg(target_arch = "x86_64")]
203impl Syscalls for X86_64 {
204    #[inline(always)]
205    unsafe fn syscall0(n: i64) -> i64 {
206        let result: i64;
207        core::arch::asm!(
208            "syscall",
209            inlateout("rax") n => result,
210            lateout("rcx") _,
211            lateout("r11") _,
212        );
213        result
214    }
215    #[inline(always)]
216    unsafe fn syscall1(n: i64, a1: i64) -> i64 {
217        let result: i64;
218        core::arch::asm!(
219            "syscall",
220            inlateout("rax") n => result,
221            in("rdi") a1,
222            lateout("rcx") _,
223            lateout("r11") _,
224        );
225        result
226    }
227    #[inline(always)]
228    unsafe fn syscall2(n: i64, a1: i64, a2: i64) -> i64 {
229        let result: i64;
230        core::arch::asm!(
231            "syscall",
232            inlateout("rax") n => result,
233            in("rdi") a1,
234            in("rsi") a2,
235            lateout("rcx") _,
236            lateout("r11") _,
237        );
238        result
239    }
240    #[inline(always)]
241    unsafe fn syscall3(n: i64, a1: i64, a2: i64, a3: i64) -> i64 {
242        let result: i64;
243        core::arch::asm!(
244            "syscall",
245            inlateout("rax") n => result,
246            in("rdi") a1,
247            in("rsi") a2,
248            in("rdx") a3,
249            lateout("rcx") _,
250            lateout("r11") _,
251        );
252        result
253    }
254    #[inline(always)]
255    unsafe fn syscall4(n: i64, a1: i64, a2: i64, a3: i64, a4: i64) -> i64 {
256        let result: i64;
257        core::arch::asm!(
258            "syscall",
259            inlateout("rax") n => result,
260            in("rdi") a1,
261            in("rsi") a2,
262            in("rdx") a3,
263            in("r10") a4,
264            lateout("rcx") _,
265            lateout("r11") _,
266        );
267        result
268    }
269    #[inline(always)]
270    unsafe fn syscall5(n: i64, a1: i64, a2: i64, a3: i64, a4: i64, a5: i64) -> i64 {
271        let result: i64;
272        core::arch::asm!(
273            "syscall",
274            inlateout("rax") n => result,
275            in("rdi") a1,
276            in("rsi") a2,
277            in("rdx") a3,
278            in("r10") a4,
279            in("r8") a5,
280            lateout("rcx") _,
281            lateout("r11") _,
282        );
283        result
284    }
285    #[inline(always)]
286    unsafe fn syscall6(n: i64, a1: i64, a2: i64, a3: i64, a4: i64, a5: i64, a6: i64) -> i64 {
287        let result: i64;
288        core::arch::asm!(
289            "syscall",
290            inlateout("rax") n => result,
291            in("rdi") a1,
292            in("rsi") a2,
293            in("rdx") a3,
294            in("r10") a4,
295            in("r8") a5,
296            in("r9") a6,
297            lateout("rcx") _,
298            lateout("r11") _,
299        );
300        result
301    }
302    #[inline(always)]
303    unsafe fn syscall_noreturn1(n: i64, a1: i64) -> ! {
304        core::arch::asm!(
305            "syscall",
306            in("rax") n,
307            in("rdi") a1,
308            options(noreturn)
309        );
310    }
311}
312
313#[cfg(target_arch = "aarch64")]
314impl Syscalls for Aarch64 {
315    #[inline(always)]
316    unsafe fn syscall0(n: i64) -> i64 {
317        let result: i64;
318        core::arch::asm!(
319            "svc #0",
320            inlateout("x8") n => _,
321            lateout("x0") result,
322            options(nostack),
323        );
324        result
325    }
326    #[inline(always)]
327    unsafe fn syscall1(n: i64, a1: i64) -> i64 {
328        let result: i64;
329        core::arch::asm!(
330            "svc #0",
331            inlateout("x8") n => _,
332            inlateout("x0") a1 => result,
333            options(nostack),
334        );
335        result
336    }
337    #[inline(always)]
338    unsafe fn syscall2(n: i64, a1: i64, a2: i64) -> i64 {
339        let result: i64;
340        core::arch::asm!(
341            "svc #0",
342            inlateout("x8") n => _,
343            inlateout("x0") a1 => result,
344            inlateout("x1") a2 => _,
345            options(nostack),
346        );
347        result
348    }
349    #[inline(always)]
350    unsafe fn syscall3(n: i64, a1: i64, a2: i64, a3: i64) -> i64 {
351        let result: i64;
352        core::arch::asm!(
353            "svc #0",
354            inlateout("x8") n => _,
355            inlateout("x0") a1 => result,
356            inlateout("x1") a2 => _,
357            inlateout("x2") a3 => _,
358            options(nostack),
359        );
360        result
361    }
362    #[inline(always)]
363    unsafe fn syscall4(n: i64, a1: i64, a2: i64, a3: i64, a4: i64) -> i64 {
364        let result: i64;
365        core::arch::asm!(
366            "svc #0",
367            inlateout("x8") n => _,
368            inlateout("x0") a1 => result,
369            inlateout("x1") a2 => _,
370            inlateout("x2") a3 => _,
371            inlateout("x3") a4 => _,
372            options(nostack),
373        );
374        result
375    }
376    #[inline(always)]
377    unsafe fn syscall5(n: i64, a1: i64, a2: i64, a3: i64, a4: i64, a5: i64) -> i64 {
378        let result: i64;
379        core::arch::asm!(
380            "svc #0",
381            inlateout("x8") n => _,
382            inlateout("x0") a1 => result,
383            inlateout("x1") a2 => _,
384            inlateout("x2") a3 => _,
385            inlateout("x3") a4 => _,
386            inlateout("x4") a5 => _,
387            options(nostack),
388        );
389        result
390    }
391    #[inline(always)]
392    unsafe fn syscall6(n: i64, a1: i64, a2: i64, a3: i64, a4: i64, a5: i64, a6: i64) -> i64 {
393        let result: i64;
394        core::arch::asm!(
395            "svc #0",
396            inlateout("x8") n => _,
397            inlateout("x0") a1 => result,
398            inlateout("x1") a2 => _,
399            inlateout("x2") a3 => _,
400            inlateout("x3") a4 => _,
401            inlateout("x4") a5 => _,
402            inlateout("x5") a6 => _,
403            options(nostack),
404        );
405        result
406    }
407    #[inline(always)]
408    unsafe fn syscall_noreturn1(n: i64, a1: i64) -> ! {
409        core::arch::asm!(
410            "svc #0",
411            in("x8") n,
412            in("x0") a1,
413            options(noreturn, nostack),
414        );
415    }
416}
417
418#[cfg(target_arch = "riscv64")]
419impl Syscalls for Riscv64 {
420    #[inline(always)]
421    unsafe fn syscall0(n: i64) -> i64 {
422        let result: i64;
423        core::arch::asm!(
424            "ecall",
425            inlateout("a7") n => _,
426            lateout("a0") result,
427            options(nostack),
428        );
429        result
430    }
431    #[inline(always)]
432    unsafe fn syscall1(n: i64, a1: i64) -> i64 {
433        let result: i64;
434        core::arch::asm!(
435            "ecall",
436            inlateout("a7") n => _,
437            inlateout("a0") a1 => result,
438            options(nostack),
439        );
440        result
441    }
442    #[inline(always)]
443    unsafe fn syscall2(n: i64, a1: i64, a2: i64) -> i64 {
444        let result: i64;
445        core::arch::asm!(
446            "ecall",
447            inlateout("a7") n => _,
448            inlateout("a0") a1 => result,
449            inlateout("a1") a2 => _,
450            options(nostack),
451        );
452        result
453    }
454    #[inline(always)]
455    unsafe fn syscall3(n: i64, a1: i64, a2: i64, a3: i64) -> i64 {
456        let result: i64;
457        core::arch::asm!(
458            "ecall",
459            inlateout("a7") n => _,
460            inlateout("a0") a1 => result,
461            inlateout("a1") a2 => _,
462            inlateout("a2") a3 => _,
463            options(nostack),
464        );
465        result
466    }
467    #[inline(always)]
468    unsafe fn syscall4(n: i64, a1: i64, a2: i64, a3: i64, a4: i64) -> i64 {
469        let result: i64;
470        core::arch::asm!(
471            "ecall",
472            inlateout("a7") n => _,
473            inlateout("a0") a1 => result,
474            inlateout("a1") a2 => _,
475            inlateout("a2") a3 => _,
476            inlateout("a3") a4 => _,
477            options(nostack),
478        );
479        result
480    }
481    #[inline(always)]
482    unsafe fn syscall5(n: i64, a1: i64, a2: i64, a3: i64, a4: i64, a5: i64) -> i64 {
483        let result: i64;
484        core::arch::asm!(
485            "ecall",
486            inlateout("a7") n => _,
487            inlateout("a0") a1 => result,
488            inlateout("a1") a2 => _,
489            inlateout("a2") a3 => _,
490            inlateout("a3") a4 => _,
491            inlateout("a4") a5 => _,
492            options(nostack),
493        );
494        result
495    }
496    #[inline(always)]
497    unsafe fn syscall6(n: i64, a1: i64, a2: i64, a3: i64, a4: i64, a5: i64, a6: i64) -> i64 {
498        let result: i64;
499        core::arch::asm!(
500            "ecall",
501            inlateout("a7") n => _,
502            inlateout("a0") a1 => result,
503            inlateout("a1") a2 => _,
504            inlateout("a2") a3 => _,
505            inlateout("a3") a4 => _,
506            inlateout("a4") a5 => _,
507            inlateout("a5") a6 => _,
508            options(nostack),
509        );
510        result
511    }
512    #[inline(always)]
513    unsafe fn syscall_noreturn1(n: i64, a1: i64) -> ! {
514        core::arch::asm!(
515            "ecall",
516            in("a7") n,
517            in("a0") a1,
518            options(noreturn, nostack),
519        );
520    }
521}
522
523#[cfg(target_arch = "x86_64")]
524type Arch = X86_64;
525#[cfg(target_arch = "aarch64")]
526type Arch = Aarch64;
527#[cfg(target_arch = "riscv64")]
528type Arch = Riscv64;
529
530
531
532// Architecture-specific syscall numbers
533#[cfg(target_arch = "x86_64")]
534mod sysnr {
535    pub const SYS_READ: i64 = 0;
536    pub const SYS_WRITE: i64 = 1;
537    pub const SYS_OPEN: i64 = 2;
538    pub const SYS_CLOSE: i64 = 3;
539    pub const SYS_STAT: i64 = 4;
540    pub const SYS_FSTAT: i64 = 5;
541    pub const SYS_LSEEK: i64 = 8;
542    pub const SYS_MMAP: i64 = 9;
543    pub const SYS_MUNMAP: i64 = 11;
544    pub const SYS_MPROTECT: i64 = 10;
545    pub const SYS_RT_SIGACTION: i64 = 13;
546    pub const SYS_RT_SIGPROCMASK: i64 = 14;
547    pub const SYS_IOCTL: i64 = 16;
548    pub const SYS_ACCESS: i64 = 21;
549    pub const SYS_SHMGET: i64 = 29;
550    pub const SYS_SHMAT: i64 = 30;
551    pub const SYS_SHMCTL: i64 = 31;
552    pub const SYS_DUP: i64 = 32;
553    pub const SYS_NANOSLEEP: i64 = 35;
554    pub const SYS_SETITIMER: i64 = 38;
555    pub const SYS_ALARM: i64 = 37;
556    pub const SYS_SOCKET: i64 = 41;
557    pub const SYS_CONNECT: i64 = 42;
558    pub const SYS_ACCEPT: i64 = 43;
559    pub const SYS_ACCEPT4: i64 = 288;
560    pub const SYS_SENDTO: i64 = 44;
561    pub const SYS_RECVFROM: i64 = 45;
562    pub const SYS_SENDMSG: i64 = 46;
563    pub const SYS_RECVMSG: i64 = 47;
564    pub const SYS_SHUTDOWN: i64 = 48;
565    pub const SYS_BIND: i64 = 49;
566    pub const SYS_LISTEN: i64 = 50;
567    pub const SYS_GETSOCKNAME: i64 = 51;
568    pub const SYS_SOCKETPAIR: i64 = 53;
569    pub const SYS_SETSOCKOPT: i64 = 54;
570    pub const SYS_EXECVE: i64 = 59;
571    pub const SYS_WAIT4: i64 = 61;
572    pub const SYS_KILL: i64 = 62;
573    pub const SYS_UNAME: i64 = 63;
574    pub const SYS_SEMGET: i64 = 64;
575    pub const SYS_SEMOP: i64 = 65;
576    pub const SYS_SEMCTL: i64 = 66;
577    pub const SYS_SHMDT: i64 = 67;
578    pub const SYS_MSGGET: i64 = 68;
579    pub const SYS_MSGSND: i64 = 69;
580    pub const SYS_MSGRCV: i64 = 70;
581    pub const SYS_MSGCTL: i64 = 71;
582    pub const SYS_FCNTL: i64 = 72;
583    pub const SYS_FSYNC: i64 = 74;
584    pub const SYS_TRUNCATE: i64 = 76;
585    pub const SYS_FTRUNCATE: i64 = 77;
586    pub const SYS_GETCWD: i64 = 79;
587    pub const SYS_CHDIR: i64 = 80;
588    pub const SYS_SYMLINK: i64 = 88;
589    pub const SYS_FCHMOD: i64 = 91;
590    pub const SYS_UMASK: i64 = 95;
591    pub const SYS_GETRLIMIT: i64 = 97;
592    pub const SYS_SETUID: i64 = 105;
593    pub const SYS_SETGID: i64 = 106;
594    pub const SYS_SETPGID: i64 = 109;
595    pub const SYS_GETGROUPS: i64 = 115;
596    pub const SYS_GETPGID: i64 = 121;
597    pub const SYS_GETSID: i64 = 124;
598    pub const SYS_RT_SIGPENDING: i64 = 127;
599    pub const SYS_RT_SIGTIMEDWAIT: i64 = 128;
600    pub const SYS_RT_SIGSUSPEND: i64 = 130;
601    pub const SYS_SIGALTSTACK: i64 = 131;
602    pub const SYS_SETRLIMIT: i64 = 160;
603    pub const SYS_SETHOSTNAME: i64 = 170;
604    pub const SYS_FUTEX: i64 = 202;
605    pub const SYS_CLOCK_GETTIME: i64 = 228;
606    pub const SYS_CLOCK_GETRES: i64 = 229;
607    pub const SYS_CLOCK_NANOSLEEP: i64 = 230;
608    pub const SYS_EXIT_GROUP: i64 = 231;
609    pub const SYS_TGKILL: i64 = 234;
610    pub const SYS_MKDIRAT: i64 = 258;
611    pub const SYS_NEWFSTATAT: i64 = 262;
612    pub const SYS_UNLINKAT: i64 = 263;
613    pub const SYS_LINKAT: i64 = 265;
614    pub const SYS_SYMLINKAT: i64 = 266;
615    pub const SYS_READLINKAT: i64 = 267;
616    pub const SYS_FCHMODAT: i64 = 268;
617    pub const SYS_SET_ROBUST_LIST: i64 = 273;
618    pub const SYS_UTIMENSAT: i64 = 280;
619    pub const SYS_OPENAT: i64 = 257;
620    pub const SYS_FACCESSAT: i64 = 269;
621    pub const SYS_DUP3: i64 = 292;
622    pub const SYS_PIPE2: i64 = 293;
623    pub const SYS_SYNCFS: i64 = 306;
624    pub const SYS_RENAMEAT2: i64 = 316;
625    pub const SYS_STATFS: i64 = 137;
626    pub const SYS_FSTATFS: i64 = 138;
627    pub const SYS_FDATASYNC: i64 = 75;
628    pub const SYS_PAUSE: i64 = 34;
629    pub const SYS_POLL: i64 = 7;
630    pub const SYS_PPOLL: i64 = 271;
631    pub const SYS_PSELECT6: i64 = 270;
632    pub const SYS_GETPID: i64 = 39;
633    pub const SYS_FORK: i64 = 57;
634    pub const SYS_EXIT: i64 = 60;
635    pub const SYS_GETPPID: i64 = 110;
636    pub const SYS_SETSID: i64 = 112;
637    pub const SYS_SYNC: i64 = 162;
638    pub const SYS_GETUID: i64 = 102;
639    pub const SYS_GETGID: i64 = 104;
640    pub const SYS_GETEUID: i64 = 107;
641    pub const SYS_GETEGID: i64 = 108;
642    pub const SYS_GETTID: i64 = 186;
643    pub const SYS_CLOCK_SETTIME: i64 = 227;
644    pub const SYS_CLONE: i64 = 56;
645    pub const SYS_PREAD64: i64 = 17;
646    pub const SYS_MREMAP: i64 = 25;
647    pub const SYS_SYSINFO: i64 = 99;
648    pub const SYS_MEMBARRIER: i64 = 324;
649    pub const SYS_VHANGUP: i64 = 153;
650    pub const SYS_VMSPLICE: i64 = 278;
651    pub const SYS_GETDENTS64: i64 = 217;
652    pub const SYS_EPOLL_CREATE: i64 = 213;
653    pub const SYS_EPOLL_CREATE1: i64 = 291;
654    pub const SYS_EPOLL_CTL: i64 = 233;
655    pub const SYS_EPOLL_WAIT: i64 = 232;
656    pub const SYS_EPOLL_PWAIT: i64 = 281;
657    pub const SYS_EVENTFD2: i64 = 290;
658    pub const SYS_SIGNALFD4: i64 = 289;
659    pub const SYS_TIMERFD_CREATE: i64 = 283;
660    pub const SYS_TIMERFD_SETTIME: i64 = 286;
661    pub const SYS_TIMERFD_GETTIME: i64 = 287;
662    pub const SYS_PRCTL: i64 = 157;
663    pub const SYS_FALLOCATE: i64 = 285;
664    pub const SYS_FLOCK: i64 = 73;
665    pub const SYS_SETGROUPS: i64 = 116;
666    pub const SYS_SETRESUID: i64 = 117;
667    pub const SYS_SETRESGID: i64 = 119;
668    pub const SYS_GETRESUID: i64 = 118;
669    pub const SYS_GETRESGID: i64 = 120;
670    pub const SYS_MKNOD: i64 = 133;
671    pub const SYS_FCHDIR: i64 = 81;
672    pub const SYS_FCHOWN: i64 = 93;
673    pub const SYS_ACCT: i64 = 163;
674    pub const SYS_ADJTIMEX: i64 = 159;
675    pub const SYS_CAPGET: i64 = 125;
676    pub const SYS_CAPSET: i64 = 126;
677    pub const SYS_CHROOT: i64 = 161;
678    pub const SYS_COPY_FILE_RANGE: i64 = 326;
679    pub const SYS_DELETE_MODULE: i64 = 176;
680    pub const SYS_INIT_MODULE: i64 = 175;
681    pub const SYS_FCHOWNAT: i64 = 260;
682    pub const SYS_MKNODAT: i64 = 259;
683    pub const SYS_SETNS: i64 = 308;
684    pub const SYS_UNSHARE: i64 = 272;
685    pub const SYS_REBOOT: i64 = 169;
686    pub const SYS_NICE: i64 = 34;
687    pub const SYS_SETPRIORITY: i64 = 141;
688    pub const SYS_GETPRIORITY: i64 = 140;
689    pub const SYS_IOPERM: i64 = 173;
690    pub const SYS_IOPL: i64 = 172;
691    pub const SYS_SWAPON: i64 = 167;
692    pub const SYS_SWAPOFF: i64 = 168;
693    pub const SYS_MOUNT: i64 = 165;
694    pub const SYS_UMOUNT2: i64 = 166;
695    pub const SYS_PIVOT_ROOT: i64 = 155;
696    pub const SYS_SETTIMEOFDAY: i64 = 164;
697    pub const SYS_GETTIMEOFDAY: i64 = 96;
698}
699#[cfg(target_arch = "aarch64")]
700mod sysnr {
701    pub const SYS_READ: i64 = 63;
702    pub const SYS_WRITE: i64 = 64;
703    // pub const SYS_OPEN: i64 = ???; // missing in aarch64 table
704    pub const SYS_CLOSE: i64 = 57;
705    // pub const SYS_STAT: i64 = ???; // missing in aarch64 table
706    pub const SYS_FSTAT: i64 = 80;
707    pub const SYS_LSEEK: i64 = 62;
708    pub const SYS_MMAP: i64 = 222;
709    pub const SYS_MUNMAP: i64 = 215;
710    pub const SYS_RT_SIGACTION: i64 = 134;
711    pub const SYS_RT_SIGPROCMASK: i64 = 135;
712    pub const SYS_IOCTL: i64 = 29;
713    // pub const SYS_ACCESS: i64 = ???; // missing in aarch64 table
714    pub const SYS_SHMGET: i64 = 194;
715    pub const SYS_SHMAT: i64 = 196;
716    pub const SYS_SHMCTL: i64 = 195;
717    pub const SYS_DUP: i64 = 23;
718    pub const SYS_NANOSLEEP: i64 = 101;
719    pub const SYS_SETITIMER: i64 = 103;
720    // pub const SYS_ALARM: i64 = ???; // missing in aarch64 table
721    pub const SYS_SOCKET: i64 = 198;
722    pub const SYS_CONNECT: i64 = 203;
723    pub const SYS_ACCEPT: i64 = 202;
724    pub const SYS_SENDTO: i64 = 206;
725    pub const SYS_RECVFROM: i64 = 207;
726    pub const SYS_SHUTDOWN: i64 = 210;
727    pub const SYS_BIND: i64 = 200;
728    pub const SYS_LISTEN: i64 = 201;
729    pub const SYS_GETSOCKNAME: i64 = 204;
730    pub const SYS_SOCKETPAIR: i64 = 199;
731    pub const SYS_SETSOCKOPT: i64 = 208;
732    pub const SYS_EXECVE: i64 = 221;
733    pub const SYS_WAIT4: i64 = 260;
734    pub const SYS_KILL: i64 = 129;
735    pub const SYS_UNAME: i64 = 160;
736    pub const SYS_SEMGET: i64 = 190;
737    pub const SYS_SEMOP: i64 = 193;
738    pub const SYS_SEMCTL: i64 = 191;
739    pub const SYS_SHMDT: i64 = 197;
740    pub const SYS_MSGGET: i64 = 186;
741    pub const SYS_MSGSND: i64 = 189;
742    pub const SYS_MSGRCV: i64 = 188;
743    pub const SYS_MSGCTL: i64 = 187;
744    pub const SYS_FCNTL: i64 = 25;
745    pub const SYS_FSYNC: i64 = 82;
746    pub const SYS_TRUNCATE: i64 = 45;
747    pub const SYS_FTRUNCATE: i64 = 46;
748    pub const SYS_GETCWD: i64 = 17;
749    pub const SYS_CHDIR: i64 = 49;
750    // pub const SYS_SYMLINK: i64 = ???; // missing in aarch64 table
751    pub const SYS_FCHMOD: i64 = 52;
752    pub const SYS_UMASK: i64 = 166;
753    pub const SYS_GETRLIMIT: i64 = 163;
754    pub const SYS_SETUID: i64 = 146;
755    pub const SYS_SETGID: i64 = 144;
756    pub const SYS_SETPGID: i64 = 154;
757    pub const SYS_GETGROUPS: i64 = 158;
758    pub const SYS_GETPGID: i64 = 155;
759    pub const SYS_GETSID: i64 = 156;
760    pub const SYS_RT_SIGPENDING: i64 = 136;
761    pub const SYS_RT_SIGTIMEDWAIT: i64 = 137;
762    pub const SYS_RT_SIGSUSPEND: i64 = 133;
763    pub const SYS_SIGALTSTACK: i64 = 132;
764    pub const SYS_SETRLIMIT: i64 = 164;
765    pub const SYS_SETHOSTNAME: i64 = 161;
766    pub const SYS_FUTEX: i64 = 98;
767    pub const SYS_CLOCK_GETTIME: i64 = 113;
768    pub const SYS_CLOCK_GETRES: i64 = 114;
769    pub const SYS_CLOCK_NANOSLEEP: i64 = 115;
770    pub const SYS_EXIT_GROUP: i64 = 94;
771    pub const SYS_TGKILL: i64 = 131;
772    pub const SYS_MKDIRAT: i64 = 34;
773    pub const SYS_NEWFSTATAT: i64 = 79;
774    pub const SYS_UNLINKAT: i64 = 35;
775    pub const SYS_LINKAT: i64 = 37;
776    pub const SYS_SYMLINKAT: i64 = 36;
777    pub const SYS_READLINKAT: i64 = 78;
778    pub const SYS_FCHMODAT: i64 = 53;
779    pub const SYS_SET_ROBUST_LIST: i64 = 99;
780    pub const SYS_UTIMENSAT: i64 = 88;
781    pub const SYS_OPENAT: i64 = 56;
782    pub const SYS_FACCESSAT: i64 = 48;
783    pub const SYS_DUP3: i64 = 24;
784    pub const SYS_PIPE2: i64 = 59;
785    pub const SYS_SYNCFS: i64 = 267;
786    pub const SYS_RENAMEAT2: i64 = 276;
787    pub const SYS_STATFS: i64 = 43;
788    pub const SYS_FSTATFS: i64 = 44;
789    pub const SYS_FDATASYNC: i64 = 83;
790    pub const SYS_GETPID: i64 = 172;
791    pub const SYS_EXIT: i64 = 93;
792    pub const SYS_GETPPID: i64 = 173;
793    pub const SYS_SETSID: i64 = 157;
794    pub const SYS_SYNC: i64 = 81;
795    pub const SYS_GETUID: i64 = 174;
796    pub const SYS_GETGID: i64 = 176;
797    pub const SYS_GETEUID: i64 = 175;
798    pub const SYS_GETEGID: i64 = 177;
799    pub const SYS_GETTID: i64 = 178;
800    pub const SYS_CLOCK_SETTIME: i64 = 112;
801    pub const SYS_CLONE: i64 = 220;
802    pub const SYS_PPOLL: i64 = 73;
803    pub const SYS_PREAD64: i64 = 67;
804    pub const SYS_MREMAP: i64 = 216;
805    pub const SYS_SYSINFO: i64 = 179;
806    pub const SYS_MEMBARRIER: i64 = 283;
807    pub const SYS_VHANGUP: i64 = 58;
808    pub const SYS_VMSPLICE: i64 = 75;
809    pub const SYS_GETTIMEOFDAY: i64 = 169;
810    pub const SYS_SETTIMEOFDAY: i64 = 170;
811    pub const SYS_ADJTIMEX: i64 = 171;
812    pub const SYS_EPOLL_CREATE1: i64 = 20;
813    pub const SYS_EPOLL_CTL: i64 = 21;
814    pub const SYS_EPOLL_WAIT: i64 = 22;
815    pub const SYS_EPOLL_PWAIT: i64 = 22;
816    pub const SYS_EVENTFD2: i64 = 19;
817    pub const SYS_SIGNALFD4: i64 = 74;
818    pub const SYS_TIMERFD_CREATE: i64 = 85;
819    pub const SYS_TIMERFD_SETTIME: i64 = 86;
820    pub const SYS_TIMERFD_GETTIME: i64 = 87;
821    pub const SYS_ACCEPT4: i64 = 242;
822    pub const SYS_ACCT: i64 = 89;
823    pub const SYS_CAPGET: i64 = 90;
824    pub const SYS_CAPSET: i64 = 91;
825    pub const SYS_CHROOT: i64 = 51;
826    pub const SYS_COPY_FILE_RANGE: i64 = 285;
827    pub const SYS_DELETE_MODULE: i64 = 106;
828    pub const SYS_FALLOCATE: i64 = 47;
829    pub const SYS_FCHDIR: i64 = 50;
830    pub const SYS_FCHOWN: i64 = 55;
831    pub const SYS_FCHOWNAT: i64 = 54;
832    pub const SYS_FLOCK: i64 = 32;
833    pub const SYS_GETDENTS64: i64 = 61;
834    pub const SYS_GETPRIORITY: i64 = 141;
835    pub const SYS_GETRESGID: i64 = 150;
836    pub const SYS_GETRESUID: i64 = 148;
837    pub const SYS_INIT_MODULE: i64 = 105;
838    pub const SYS_MKNODAT: i64 = 33;
839    pub const SYS_MOUNT: i64 = 40;
840    pub const SYS_MPROTECT: i64 = 226;
841    pub const SYS_PIVOT_ROOT: i64 = 41;
842    pub const SYS_PRCTL: i64 = 167;
843    pub const SYS_PSELECT6: i64 = 72;
844    pub const SYS_REBOOT: i64 = 142;
845    pub const SYS_RECVMSG: i64 = 212;
846    pub const SYS_SENDMSG: i64 = 211;
847    pub const SYS_SETGROUPS: i64 = 159;
848    pub const SYS_SETNS: i64 = 268;
849    pub const SYS_SETPRIORITY: i64 = 140;
850    pub const SYS_SETRESGID: i64 = 149;
851    pub const SYS_SETRESUID: i64 = 147;
852    pub const SYS_SWAPOFF: i64 = 225;
853    pub const SYS_SWAPON: i64 = 224;
854    pub const SYS_UMOUNT2: i64 = 39;
855    pub const SYS_UNSHARE: i64 = 97;
856}
857// riscv64 uses the same new-style syscall table as aarch64
858#[cfg(target_arch = "riscv64")]
859mod sysnr {
860    pub const SYS_READ: i64 = 63;
861    pub const SYS_WRITE: i64 = 64;
862    pub const SYS_CLOSE: i64 = 57;
863    pub const SYS_FSTAT: i64 = 80;
864    pub const SYS_LSEEK: i64 = 62;
865    pub const SYS_MMAP: i64 = 222;
866    pub const SYS_MUNMAP: i64 = 215;
867    pub const SYS_RT_SIGACTION: i64 = 134;
868    pub const SYS_RT_SIGPROCMASK: i64 = 135;
869    pub const SYS_IOCTL: i64 = 29;
870    pub const SYS_SHMGET: i64 = 194;
871    pub const SYS_SHMAT: i64 = 196;
872    pub const SYS_SHMCTL: i64 = 195;
873    pub const SYS_DUP: i64 = 23;
874    pub const SYS_NANOSLEEP: i64 = 101;
875    pub const SYS_SETITIMER: i64 = 103;
876    pub const SYS_SOCKET: i64 = 198;
877    pub const SYS_CONNECT: i64 = 203;
878    pub const SYS_ACCEPT: i64 = 202;
879    pub const SYS_SENDTO: i64 = 206;
880    pub const SYS_RECVFROM: i64 = 207;
881    pub const SYS_SHUTDOWN: i64 = 210;
882    pub const SYS_BIND: i64 = 200;
883    pub const SYS_LISTEN: i64 = 201;
884    pub const SYS_GETSOCKNAME: i64 = 204;
885    pub const SYS_SOCKETPAIR: i64 = 199;
886    pub const SYS_SETSOCKOPT: i64 = 208;
887    pub const SYS_EXECVE: i64 = 221;
888    pub const SYS_WAIT4: i64 = 260;
889    pub const SYS_KILL: i64 = 129;
890    pub const SYS_UNAME: i64 = 160;
891    pub const SYS_SEMGET: i64 = 190;
892    pub const SYS_SEMOP: i64 = 193;
893    pub const SYS_SEMCTL: i64 = 191;
894    pub const SYS_SHMDT: i64 = 197;
895    pub const SYS_MSGGET: i64 = 186;
896    pub const SYS_MSGSND: i64 = 189;
897    pub const SYS_MSGRCV: i64 = 188;
898    pub const SYS_MSGCTL: i64 = 187;
899    pub const SYS_FCNTL: i64 = 25;
900    pub const SYS_FSYNC: i64 = 82;
901    pub const SYS_TRUNCATE: i64 = 45;
902    pub const SYS_FTRUNCATE: i64 = 46;
903    pub const SYS_GETCWD: i64 = 17;
904    pub const SYS_CHDIR: i64 = 49;
905    pub const SYS_FCHMOD: i64 = 52;
906    pub const SYS_UMASK: i64 = 166;
907    pub const SYS_GETRLIMIT: i64 = 163;
908    pub const SYS_SETUID: i64 = 146;
909    pub const SYS_SETGID: i64 = 144;
910    pub const SYS_SETPGID: i64 = 154;
911    pub const SYS_GETGROUPS: i64 = 158;
912    pub const SYS_GETPGID: i64 = 155;
913    pub const SYS_GETSID: i64 = 156;
914    pub const SYS_RT_SIGPENDING: i64 = 136;
915    pub const SYS_RT_SIGTIMEDWAIT: i64 = 137;
916    pub const SYS_RT_SIGSUSPEND: i64 = 133;
917    pub const SYS_SIGALTSTACK: i64 = 132;
918    pub const SYS_SETRLIMIT: i64 = 164;
919    pub const SYS_SETHOSTNAME: i64 = 161;
920    pub const SYS_FUTEX: i64 = 98;
921    pub const SYS_CLOCK_GETTIME: i64 = 113;
922    pub const SYS_CLOCK_GETRES: i64 = 114;
923    pub const SYS_CLOCK_NANOSLEEP: i64 = 115;
924    pub const SYS_EXIT_GROUP: i64 = 94;
925    pub const SYS_TGKILL: i64 = 131;
926    pub const SYS_MKDIRAT: i64 = 34;
927    pub const SYS_NEWFSTATAT: i64 = 79;
928    pub const SYS_UNLINKAT: i64 = 35;
929    pub const SYS_LINKAT: i64 = 37;
930    pub const SYS_SYMLINKAT: i64 = 36;
931    pub const SYS_READLINKAT: i64 = 78;
932    pub const SYS_FCHMODAT: i64 = 53;
933    pub const SYS_SET_ROBUST_LIST: i64 = 99;
934    pub const SYS_UTIMENSAT: i64 = 88;
935    pub const SYS_OPENAT: i64 = 56;
936    pub const SYS_FACCESSAT: i64 = 48;
937    pub const SYS_DUP3: i64 = 24;
938    pub const SYS_PIPE2: i64 = 59;
939    pub const SYS_SYNCFS: i64 = 267;
940    pub const SYS_RENAMEAT2: i64 = 276;
941    pub const SYS_STATFS: i64 = 43;
942    pub const SYS_FSTATFS: i64 = 44;
943    pub const SYS_FDATASYNC: i64 = 83;
944    pub const SYS_GETPID: i64 = 172;
945    pub const SYS_EXIT: i64 = 93;
946    pub const SYS_GETPPID: i64 = 173;
947    pub const SYS_SETSID: i64 = 157;
948    pub const SYS_SYNC: i64 = 81;
949    pub const SYS_GETUID: i64 = 174;
950    pub const SYS_GETGID: i64 = 176;
951    pub const SYS_GETEUID: i64 = 175;
952    pub const SYS_GETEGID: i64 = 177;
953    pub const SYS_GETTID: i64 = 178;
954    pub const SYS_CLOCK_SETTIME: i64 = 112;
955    pub const SYS_CLONE: i64 = 220;
956    pub const SYS_PPOLL: i64 = 73;
957    pub const SYS_PREAD64: i64 = 67;
958    pub const SYS_MREMAP: i64 = 216;
959    pub const SYS_SYSINFO: i64 = 179;
960    pub const SYS_MEMBARRIER: i64 = 283;
961    pub const SYS_VHANGUP: i64 = 58;
962    pub const SYS_VMSPLICE: i64 = 75;
963    pub const SYS_GETTIMEOFDAY: i64 = 169;
964    pub const SYS_SETTIMEOFDAY: i64 = 170;
965    pub const SYS_ADJTIMEX: i64 = 171;
966    pub const SYS_EPOLL_CREATE1: i64 = 20;
967    pub const SYS_EPOLL_CTL: i64 = 21;
968    pub const SYS_EPOLL_WAIT: i64 = 22;
969    pub const SYS_EPOLL_PWAIT: i64 = 22;
970    pub const SYS_EVENTFD2: i64 = 19;
971    pub const SYS_SIGNALFD4: i64 = 74;
972    pub const SYS_TIMERFD_CREATE: i64 = 85;
973    pub const SYS_TIMERFD_SETTIME: i64 = 86;
974    pub const SYS_TIMERFD_GETTIME: i64 = 87;
975    pub const SYS_ACCEPT4: i64 = 242;
976    pub const SYS_ACCT: i64 = 89;
977    pub const SYS_CAPGET: i64 = 90;
978    pub const SYS_CAPSET: i64 = 91;
979    pub const SYS_CHROOT: i64 = 51;
980    pub const SYS_COPY_FILE_RANGE: i64 = 285;
981    pub const SYS_DELETE_MODULE: i64 = 106;
982    pub const SYS_FALLOCATE: i64 = 47;
983    pub const SYS_FCHDIR: i64 = 50;
984    pub const SYS_FCHOWN: i64 = 55;
985    pub const SYS_FCHOWNAT: i64 = 54;
986    pub const SYS_FLOCK: i64 = 32;
987    pub const SYS_GETDENTS64: i64 = 61;
988    pub const SYS_GETPRIORITY: i64 = 141;
989    pub const SYS_GETRESGID: i64 = 150;
990    pub const SYS_GETRESUID: i64 = 148;
991    pub const SYS_INIT_MODULE: i64 = 105;
992    pub const SYS_MKNODAT: i64 = 33;
993    pub const SYS_MOUNT: i64 = 40;
994    pub const SYS_MPROTECT: i64 = 226;
995    pub const SYS_PIVOT_ROOT: i64 = 41;
996    pub const SYS_PRCTL: i64 = 167;
997    pub const SYS_PSELECT6: i64 = 72;
998    pub const SYS_REBOOT: i64 = 142;
999    pub const SYS_RECVMSG: i64 = 212;
1000    pub const SYS_SENDMSG: i64 = 211;
1001    pub const SYS_SETGROUPS: i64 = 159;
1002    pub const SYS_SETNS: i64 = 268;
1003    pub const SYS_SETPRIORITY: i64 = 140;
1004    pub const SYS_SETRESGID: i64 = 149;
1005    pub const SYS_SETRESUID: i64 = 147;
1006    pub const SYS_SWAPOFF: i64 = 225;
1007    pub const SYS_SWAPON: i64 = 224;
1008    pub const SYS_UMOUNT2: i64 = 39;
1009    pub const SYS_UNSHARE: i64 = 97;
1010}
1011pub use sysnr::*;
1012
1013// ============================================================
1014// Syscall wrappers (raw, no_std)
1015// ============================================================
1016
1017#[inline]
1018unsafe fn sys_write(fd: i64, buf: *const u8, count: usize) -> i64 {
1019    <Arch as Syscalls>::syscall3(SYS_WRITE, fd as i64, buf as i64, count as i64)
1020}
1021
1022#[inline]
1023unsafe fn sys_read(fd: i64, buf: *mut u8, count: usize) -> i64 {
1024    <Arch as Syscalls>::syscall3(SYS_READ, fd as i64, buf as i64, count as i64)
1025}
1026
1027#[inline]
1028unsafe fn sys_open(path: *const u8, flags: i64, mode: i64) -> i64 {
1029    <Arch as Syscalls>::syscall4(SYS_OPENAT, AT_FDCWD as i64, path as i64, flags as i64, mode as i64)
1030}
1031
1032#[inline]
1033unsafe fn sys_close(fd: i64) -> i64 {
1034    <Arch as Syscalls>::syscall1(SYS_CLOSE, fd as i64)
1035}
1036
1037#[inline]
1038unsafe fn sys_lseek(fd: i64, offset: i64, whence: i64) -> i64 {
1039    <Arch as Syscalls>::syscall3(SYS_LSEEK, fd as i64, offset as i64, whence as i64)
1040}
1041
1042unsafe fn sys_mmap(
1043    addr: *mut u8,
1044    length: usize,
1045    prot: i32,
1046    flags: i32,
1047    fd: i32,
1048    offset: i64,
1049) -> *mut u8 {
1050    let result = <Arch as Syscalls>::syscall6(SYS_MMAP, addr as i64, length as i64, prot as i64, flags as i64, fd as i64, offset);
1051    if result < 0 && result > -4096 {
1052        ERRNO!() = (-result) as c_int;
1053        return MMAP_FAILED;
1054    }
1055    result as *mut u8
1056}
1057
1058unsafe fn sys_munmap(addr: *mut u8, length: usize) -> i64 {
1059    <Arch as Syscalls>::syscall2(SYS_MUNMAP, addr as i64, length as i64)
1060}
1061
1062// ============================================================
1063// String/memory functions
1064// ============================================================
1065
1066// strlen, memcpy, memset, memmove and memcmp live in string_fast.rs.
1067
1068#[no_mangle]
1069pub unsafe extern "C" fn bcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int {
1070    memcmp(s1, s2, n)
1071}
1072
1073// strcmp lives in string_fast.rs.
1074
1075#[no_mangle]
1076pub unsafe extern "C" fn strncmp(s1: *const u8, s2: *const u8, n: usize) -> c_int {
1077    let mut i = 0;
1078    while i < n {
1079        let a = *s1.add(i);
1080        let b = *s2.add(i);
1081        if a != b {
1082            return a as c_int - b as c_int;
1083        }
1084        if a == 0 {
1085            return 0;
1086        }
1087        i += 1;
1088    }
1089    0
1090}
1091
1092#[no_mangle]
1093pub unsafe extern "C" fn strcpy(dst: *mut u8, src: *const u8) -> *mut u8 {
1094    let mut i = 0;
1095    loop {
1096        let c = *src.add(i);
1097        *dst.add(i) = c;
1098        if c == 0 {
1099            break;
1100        }
1101        i += 1;
1102    }
1103    dst
1104}
1105
1106#[no_mangle]
1107pub unsafe extern "C" fn strncpy(dst: *mut u8, src: *const u8, n: usize) -> *mut u8 {
1108    let mut i = 0;
1109    while i < n && *src.add(i) != 0 {
1110        *dst.add(i) = *src.add(i);
1111        i += 1;
1112    }
1113    while i < n {
1114        *dst.add(i) = 0;
1115        i += 1;
1116    }
1117    dst
1118}
1119
1120#[no_mangle]
1121pub unsafe extern "C" fn strcat(dst: *mut u8, src: *const u8) -> *mut u8 {
1122    let mut i = 0;
1123    while *dst.add(i) != 0 {
1124        i += 1;
1125    }
1126    let mut j = 0;
1127    loop {
1128        let c = *src.add(j);
1129        *dst.add(i + j) = c;
1130        if c == 0 {
1131            break;
1132        }
1133        j += 1;
1134    }
1135    dst
1136}
1137
1138#[no_mangle]
1139pub unsafe extern "C" fn strncat(dst: *mut u8, src: *const u8, n: usize) -> *mut u8 {
1140    let mut i = 0;
1141    while *dst.add(i) != 0 {
1142        i += 1;
1143    }
1144    let mut j = 0;
1145    while j < n && *src.add(j) != 0 {
1146        *dst.add(i + j) = *src.add(j);
1147        j += 1;
1148    }
1149    *dst.add(i + j) = 0;
1150    dst
1151}
1152
1153#[no_mangle]
1154pub unsafe extern "C" fn strlcpy(dst: *mut u8, src: *const u8, n: usize) -> usize {
1155    let l = strlen(src as *const c_char);
1156    if n > 0 {
1157        let c = if l < n { l } else { n - 1 };
1158        core::ptr::copy_nonoverlapping(src, dst, c);
1159        *dst.add(c) = 0;
1160    }
1161    l
1162}
1163
1164#[no_mangle]
1165pub unsafe extern "C" fn strlcat(dst: *mut u8, src: *const u8, n: usize) -> usize {
1166    let dl = strlen(dst as *const c_char);
1167    let sl = strlen(src as *const c_char);
1168    if dl >= n { return n + sl; }
1169    let c = if sl < n - dl { sl } else { n - dl - 1 };
1170    core::ptr::copy_nonoverlapping(src, dst.add(dl), c);
1171    *dst.add(dl + c) = 0;
1172    dl + sl
1173}
1174
1175#[no_mangle]
1176pub unsafe extern "C" fn bcopy(src: *const c_void, dst: *mut c_void, n: usize) {
1177    core::ptr::copy(src as *const u8, dst as *mut u8, n);
1178}
1179
1180#[no_mangle]
1181pub unsafe extern "C" fn bzero(s: *mut c_void, n: usize) {
1182    core::ptr::write_bytes(s as *mut u8, 0, n);
1183}
1184
1185#[no_mangle]
1186pub unsafe extern "C" fn strndup(s: *const c_char, n: usize) -> *mut c_char {
1187    let mut len = 0;
1188    while len < n && *s.add(len) != 0 {
1189        len += 1;
1190    }
1191    let p = malloc(len + 1) as *mut c_char;
1192    if p.is_null() {
1193        return core::ptr::null_mut();
1194    }
1195    core::ptr::copy_nonoverlapping(s, p, len);
1196    *p.add(len) = 0;
1197    p
1198}
1199
1200#[no_mangle]
1201pub unsafe extern "C" fn strchr(s: *const u8, c: c_int) -> *mut u8 {
1202    let target = c as u8;
1203    let mut i = 0;
1204    loop {
1205        let ch = *s.add(i);
1206        if ch == target {
1207            return s.add(i) as *mut u8;
1208        }
1209        if ch == 0 {
1210            return null_mut();
1211        }
1212        i += 1;
1213    }
1214}
1215
1216#[no_mangle]
1217pub unsafe extern "C" fn strrchr(s: *const u8, c: c_int) -> *mut u8 {
1218    let target = c as u8;
1219    let mut i = 0;
1220    let mut last: *mut u8 = null_mut();
1221    loop {
1222        let ch = *s.add(i);
1223        if ch == target {
1224            last = s.add(i) as *mut u8;
1225        }
1226        if ch == 0 {
1227            return last;
1228        }
1229        i += 1;
1230    }
1231}
1232
1233#[no_mangle]
1234pub unsafe extern "C" fn strnlen(s: *const u8, maxlen: usize) -> usize {
1235    let mut i = 0;
1236    while i < maxlen && *s.add(i) != 0 {
1237        i += 1;
1238    }
1239    i
1240}
1241
1242#[no_mangle]
1243pub unsafe extern "C" fn strcspn(s: *const u8, reject: *const u8) -> usize {
1244    let mut i = 0;
1245    loop {
1246        let c = *s.add(i);
1247        if c == 0 {
1248            return i;
1249        }
1250        let mut j = 0;
1251        loop {
1252            let r = *reject.add(j);
1253            if r == 0 {
1254                break;
1255            }
1256            if c == r {
1257                return i;
1258            }
1259            j += 1;
1260        }
1261        i += 1;
1262    }
1263}
1264
1265#[no_mangle]
1266pub unsafe extern "C" fn strspn(s: *const u8, accept: *const u8) -> usize {
1267    let mut i = 0;
1268    loop {
1269        let c = *s.add(i);
1270        if c == 0 {
1271            return i;
1272        }
1273        let mut found = false;
1274        let mut j = 0;
1275        loop {
1276            let a = *accept.add(j);
1277            if a == 0 {
1278                break;
1279            }
1280            if c == a {
1281                found = true;
1282                break;
1283            }
1284            j += 1;
1285        }
1286        if !found {
1287            return i;
1288        }
1289        i += 1;
1290    }
1291}
1292
1293#[no_mangle]
1294pub unsafe extern "C" fn strpbrk(s: *const u8, accept: *const u8) -> *mut u8 {
1295    let mut i = 0;
1296    loop {
1297        let c = *s.add(i);
1298        if c == 0 {
1299            return null_mut();
1300        }
1301        let mut j = 0;
1302        loop {
1303            let a = *accept.add(j);
1304            if a == 0 {
1305                break;
1306            }
1307            if c == a {
1308                return s.add(i) as *mut u8;
1309            }
1310            j += 1;
1311        }
1312        i += 1;
1313    }
1314}
1315
1316#[no_mangle]
1317pub unsafe extern "C" fn strstr(haystack: *const u8, needle: *const u8) -> *mut u8 {
1318    if *needle == 0 {
1319        return haystack as *mut u8;
1320    }
1321    let mut i = 0;
1322    loop {
1323        let h = *haystack.add(i);
1324        if h == 0 {
1325            return null_mut();
1326        }
1327        if h == *needle {
1328            let mut j = 0;
1329            loop {
1330                let n = *needle.add(j);
1331                if n == 0 {
1332                    return haystack.add(i) as *mut u8;
1333                }
1334                if *haystack.add(i + j) != n {
1335                    break;
1336                }
1337                j += 1;
1338            }
1339        }
1340        i += 1;
1341    }
1342}
1343
1344#[no_mangle]
1345pub unsafe extern "C" fn memchr(s: *const u8, c: c_int, n: usize) -> *mut u8 {
1346    let target = c as u8;
1347    let mut i = 0;
1348    while i < n {
1349        if *s.add(i) == target {
1350            return s.add(i) as *mut u8;
1351        }
1352        i += 1;
1353    }
1354    null_mut()
1355}
1356
1357#[no_mangle]
1358pub unsafe extern "C" fn memrchr(s: *const u8, c: c_int, n: usize) -> *mut u8 {
1359    let target = c as u8;
1360    let mut i = n;
1361    while i > 0 {
1362        i -= 1;
1363        if *s.add(i) == target {
1364            return s.add(i) as *mut u8;
1365        }
1366    }
1367    null_mut()
1368}
1369
1370#[no_mangle]
1371pub unsafe extern "C" fn memmem(
1372    haystack: *const c_void, haystacklen: usize,
1373    needle: *const c_void, needlelen: usize,
1374) -> *mut c_void {
1375    if needlelen == 0 { return haystack as *mut c_void; }
1376    if haystacklen < needlelen { return null_mut(); }
1377    let h = haystack as *const u8;
1378    let n = needle as *const u8;
1379    let last = haystacklen - needlelen;
1380    for i in 0..=last {
1381        if *h.add(i) == *n && memcmp(h.add(i) as *const c_void, n as *const c_void, needlelen) == 0 {
1382            return h.add(i) as *mut c_void;
1383        }
1384    }
1385    null_mut()
1386}
1387
1388#[no_mangle]
1389pub unsafe extern "C" fn strtok(s: *mut u8, delim: *const u8) -> *mut u8 {
1390    static mut STATE: *mut u8 = core::ptr::null_mut();
1391    let mut p = if s.is_null() { STATE } else { s };
1392    if p.is_null() {
1393        return core::ptr::null_mut();
1394    }
1395    loop {
1396        let c = *p;
1397        if c == 0 {
1398            STATE = core::ptr::null_mut();
1399            return core::ptr::null_mut();
1400        }
1401        let mut is_delim = false;
1402        let mut i = 0;
1403        loop {
1404            let d = *delim.add(i);
1405            if d == 0 {
1406                break;
1407            }
1408            if c == d {
1409                is_delim = true;
1410                break;
1411            }
1412            i += 1;
1413        }
1414        if !is_delim {
1415            break;
1416        }
1417        p = p.add(1);
1418    }
1419    let start = p;
1420    loop {
1421        let c = *p;
1422        if c == 0 {
1423            STATE = core::ptr::null_mut();
1424            return start;
1425        }
1426        let mut is_delim = false;
1427        let mut i = 0;
1428        loop {
1429            let d = *delim.add(i);
1430            if d == 0 {
1431                break;
1432            }
1433            if c == d {
1434                is_delim = true;
1435                break;
1436            }
1437            i += 1;
1438        }
1439        if is_delim {
1440            *p = 0;
1441            STATE = p.add(1);
1442            return start;
1443        }
1444        p = p.add(1);
1445    }
1446}
1447
1448// ============================================================
1449// C type aliases
1450// ============================================================
1451
1452type SizeT = usize;
1453type SSizeT = isize;
1454type mode_t = c_uint;
1455type TimeT = c_long;
1456type ClockT = c_long;
1457type wchar_t = c_int;
1458type wint_t = c_uint;
1459type wctype_t = *const c_int;
1460type wctrans_t = *const c_int;
1461
1462const WEOF: wint_t = 0xffffffff;
1463
1464#[repr(C)]
1465pub struct tm {
1466    pub tm_sec: c_int,
1467    pub tm_min: c_int,
1468    pub tm_hour: c_int,
1469    pub tm_mday: c_int,
1470    pub tm_mon: c_int,
1471    pub tm_year: c_int,
1472    pub tm_wday: c_int,
1473    pub tm_yday: c_int,
1474    pub tm_isdst: c_int,
1475    pub tm_gmtoff: c_long,
1476    pub tm_zone: *const c_char,
1477}
1478
1479#[repr(C)]
1480pub struct timeval {
1481    pub tv_sec: TimeT,
1482    pub tv_usec: c_long,
1483}
1484
1485#[repr(C)]
1486pub struct Timeb {
1487    pub time: TimeT,
1488    pub millitm: u16,
1489    pub timezone: i16,
1490    pub dstflag: u16,
1491}
1492
1493// ============================================================
1494// ctype.h
1495// ============================================================
1496
1497const CT_UPPER: u8 = 1 << 0;
1498const CT_LOWER: u8 = 1 << 1;
1499const CT_DIGIT: u8 = 1 << 2;
1500const CT_SPACE: u8 = 1 << 3;
1501const CT_PUNCT: u8 = 1 << 4;
1502const CT_CNTRL: u8 = 1 << 5;
1503const CT_BLANK: u8 = 1 << 6;
1504const CT_XDIGIT: u8 = 1 << 7;
1505
1506const CT_TABLE: [u8; 128] = {
1507    let mut t = [0u8; 128];
1508    let mut i = 0;
1509    while i < 128 {
1510        let c = i as u8;
1511        let mut flags = 0u8;
1512        if c >= b'A' && c <= b'Z' {
1513            flags |= CT_UPPER;
1514        }
1515        if c >= b'a' && c <= b'z' {
1516            flags |= CT_LOWER;
1517        }
1518        if c >= b'0' && c <= b'9' {
1519            flags |= CT_DIGIT;
1520        }
1521        if c == b' ' || c == b'\t' || c == b'\n' || c == b'\r' || c == b'\x0c' || c == b'\x0b' {
1522            flags |= CT_SPACE;
1523        }
1524        if (c >= b'!' && c <= b'/')
1525            || (c >= b':' && c <= b'@')
1526            || (c >= b'[' && c <= b'`')
1527            || (c >= b'{' && c <= b'~')
1528        {
1529            flags |= CT_PUNCT;
1530        }
1531        if c < 0x20 || c == 0x7f {
1532            flags |= CT_CNTRL;
1533        }
1534        if c == b' ' || c == b'\t' {
1535            flags |= CT_BLANK;
1536        }
1537        if (c >= b'0' && c <= b'9') || (c >= b'A' && c <= b'F') || (c >= b'a' && c <= b'f') {
1538            flags |= CT_XDIGIT;
1539        }
1540        t[i] = flags;
1541        i += 1;
1542    }
1543    t
1544};
1545
1546#[inline]
1547fn ct_flags(c: c_int) -> u8 {
1548    if c < 0 || c > 127 {
1549        0
1550    } else {
1551        CT_TABLE[c as usize]
1552    }
1553}
1554
1555#[no_mangle]
1556pub extern "C" fn isalnum(c: c_int) -> c_int {
1557    (ct_flags(c) & (CT_UPPER | CT_LOWER | CT_DIGIT) != 0) as c_int
1558}
1559
1560#[no_mangle]
1561pub extern "C" fn isalpha(c: c_int) -> c_int {
1562    (ct_flags(c) & (CT_UPPER | CT_LOWER) != 0) as c_int
1563}
1564
1565#[no_mangle]
1566pub extern "C" fn isascii(c: c_int) -> c_int {
1567    ((c as u32) <= 127) as c_int
1568}
1569
1570#[no_mangle]
1571pub extern "C" fn isblank(c: c_int) -> c_int {
1572    (ct_flags(c) & CT_BLANK != 0) as c_int
1573}
1574
1575#[no_mangle]
1576pub extern "C" fn iscntrl(c: c_int) -> c_int {
1577    (ct_flags(c) & CT_CNTRL != 0) as c_int
1578}
1579
1580#[no_mangle]
1581pub extern "C" fn isdigit(c: c_int) -> c_int {
1582    (ct_flags(c) & CT_DIGIT != 0) as c_int
1583}
1584
1585#[no_mangle]
1586pub extern "C" fn isgraph(c: c_int) -> c_int {
1587    (ct_flags(c) & (CT_UPPER | CT_LOWER | CT_DIGIT | CT_PUNCT) != 0) as c_int
1588}
1589
1590#[no_mangle]
1591pub extern "C" fn islower(c: c_int) -> c_int {
1592    (ct_flags(c) & CT_LOWER != 0) as c_int
1593}
1594
1595#[no_mangle]
1596pub extern "C" fn isprint(c: c_int) -> c_int {
1597    let flags = ct_flags(c);
1598    (flags & (CT_UPPER | CT_LOWER | CT_DIGIT | CT_PUNCT | CT_BLANK) != 0) as c_int
1599}
1600
1601#[no_mangle]
1602pub extern "C" fn ispunct(c: c_int) -> c_int {
1603    (ct_flags(c) & CT_PUNCT != 0) as c_int
1604}
1605
1606#[no_mangle]
1607pub extern "C" fn isspace(c: c_int) -> c_int {
1608    (ct_flags(c) & CT_SPACE != 0) as c_int
1609}
1610
1611#[no_mangle]
1612pub extern "C" fn isupper(c: c_int) -> c_int {
1613    (ct_flags(c) & CT_UPPER != 0) as c_int
1614}
1615
1616#[no_mangle]
1617pub extern "C" fn isxdigit(c: c_int) -> c_int {
1618    (ct_flags(c) & CT_XDIGIT != 0) as c_int
1619}
1620
1621#[no_mangle]
1622pub extern "C" fn tolower(c: c_int) -> c_int {
1623    if (ct_flags(c) & CT_UPPER) != 0 {
1624        c + (b'a' as c_int - b'A' as c_int)
1625    } else {
1626        c
1627    }
1628}
1629
1630#[no_mangle]
1631pub extern "C" fn toupper(c: c_int) -> c_int {
1632    if (ct_flags(c) & CT_LOWER) != 0 {
1633        c - (b'a' as c_int - b'A' as c_int)
1634    } else {
1635        c
1636    }
1637}
1638
1639// ============================================================
1640// stdlib.h: integer conversion, abs, rand
1641// ============================================================
1642
1643unsafe fn parse_digit(c: u8, base: c_int) -> Option<u8> {
1644    if c == 0 { return None; }
1645    let d = if c >= b'0' && c <= b'9' {
1646        c - b'0'
1647    } else if c >= b'a' && c <= b'z' {
1648        c - b'a' + 10
1649    } else if c >= b'A' && c <= b'Z' {
1650        c - b'A' + 10
1651    } else {
1652        return None;
1653    };
1654    if (d as c_int) < base {
1655        Some(d)
1656    } else {
1657        None
1658    }
1659}
1660
1661unsafe fn parse_prefix(s: *const u8, base: *mut c_int) -> *const u8 {
1662    let mut p = s;
1663    if *base == 0 {
1664        if *p == b'0' {
1665            if (*p.add(1) == b'x' || *p.add(1) == b'X') && parse_digit(*p.add(2), 16).is_some() {
1666                *base = 16;
1667                p = p.add(2);
1668            } else {
1669                *base = 8;
1670            }
1671        } else {
1672            *base = 10;
1673        }
1674    } else if *base == 16 {
1675        if *p == b'0' && (*p.add(1) == b'x' || *p.add(1) == b'X') && parse_digit(*p.add(2), 16).is_some() {
1676            p = p.add(2);
1677        }
1678    }
1679    p
1680}
1681
1682unsafe fn strtox(
1683    s: *const c_char,
1684    endptr: *mut *mut c_char,
1685    mut base: c_int,
1686    pos_limit: u64,
1687    neg_limit: u64,
1688) -> (u64, bool, bool) {
1689    let s0 = s as *const u8;
1690    let mut p = s0;
1691    while isspace(*p as c_int) != 0 {
1692        p = p.add(1);
1693    }
1694
1695    if base < 0 || base == 1 || base > 36 {
1696        if !endptr.is_null() {
1697            *endptr = s as *mut c_char;
1698        }
1699        ERRNO!() = EINVAL;
1700        return (0, false, false);
1701    }
1702
1703    let mut neg = false;
1704    match *p {
1705        b'-' => { neg = true; p = p.add(1); }
1706        b'+' => p = p.add(1),
1707        _ => {}
1708    }
1709
1710    p = parse_prefix(p, &mut base);
1711
1712    let limit = if neg { neg_limit } else { pos_limit };
1713    let mut val: u64 = 0;
1714    let mut any = false;
1715    while let Some(d) = parse_digit(*p, base) {
1716        any = true;
1717        if val > (limit - d as u64) / base as u64 {
1718            while parse_digit(*p, base).is_some() { p = p.add(1); }
1719            if !endptr.is_null() { *endptr = p as *mut c_char; }
1720            ERRNO!() = ERANGE_VAL;
1721            return (limit, true, neg);
1722        }
1723        val = val * base as u64 + d as u64;
1724        p = p.add(1);
1725    }
1726
1727    if !endptr.is_null() {
1728        *endptr = if any { p as *mut c_char } else { s as *mut c_char };
1729    }
1730    (val, false, neg)
1731}
1732
1733#[no_mangle]
1734pub unsafe extern "C" fn strtol(s: *const c_char, endptr: *mut *mut c_char, base: c_int) -> c_long {
1735    let pos_limit = c_long::MAX as u64;
1736    let neg_limit = pos_limit.wrapping_add(1);
1737    let (val, overflow, neg) = strtox(s, endptr, base, pos_limit, neg_limit);
1738    if overflow {
1739        return if neg { c_long::MIN } else { c_long::MAX };
1740    }
1741    if neg { val.wrapping_neg() as c_long } else { val as c_long }
1742}
1743
1744#[no_mangle]
1745pub unsafe extern "C" fn strtoul(s: *const c_char, endptr: *mut *mut c_char, base: c_int) -> c_ulong {
1746    let limit = c_ulong::MAX as u64;
1747    let (val, overflow, neg) = strtox(s, endptr, base, limit, limit);
1748    if overflow {
1749        return c_ulong::MAX;
1750    }
1751    if neg { val.wrapping_neg() as c_ulong } else { val as c_ulong }
1752}
1753
1754#[no_mangle]
1755pub unsafe extern "C" fn strtoll(s: *const c_char, endptr: *mut *mut c_char, base: c_int) -> c_longlong {
1756    let pos_limit = c_longlong::MAX as u64;
1757    let neg_limit = pos_limit.wrapping_add(1);
1758    let (val, overflow, neg) = strtox(s, endptr, base, pos_limit, neg_limit);
1759    if overflow {
1760        return if neg { c_longlong::MIN } else { c_longlong::MAX };
1761    }
1762    if neg { val.wrapping_neg() as c_longlong } else { val as c_longlong }
1763}
1764
1765#[no_mangle]
1766pub unsafe extern "C" fn strtoull(s: *const c_char, endptr: *mut *mut c_char, base: c_int) -> c_ulonglong {
1767    let limit = c_ulonglong::MAX as u64;
1768    let (val, overflow, neg) = strtox(s, endptr, base, limit, limit);
1769    if overflow {
1770        return c_ulonglong::MAX;
1771    }
1772    if neg { val.wrapping_neg() as c_ulonglong } else { val as c_ulonglong }
1773}
1774
1775#[no_mangle]
1776pub unsafe extern "C" fn atoi(s: *const c_char) -> c_int {
1777    strtol(s, core::ptr::null_mut(), 10) as c_int
1778}
1779
1780#[no_mangle]
1781pub unsafe extern "C" fn atol(s: *const c_char) -> c_long {
1782    strtol(s, core::ptr::null_mut(), 10)
1783}
1784
1785#[no_mangle]
1786pub unsafe extern "C" fn atoll(s: *const c_char) -> c_longlong {
1787    strtoll(s, core::ptr::null_mut(), 10)
1788}
1789
1790#[no_mangle]
1791pub extern "C" fn abs(n: c_int) -> c_int {
1792    if n < 0 {
1793        -n
1794    } else {
1795        n
1796    }
1797}
1798
1799#[no_mangle]
1800pub extern "C" fn labs(n: c_long) -> c_long {
1801    if n < 0 {
1802        -n
1803    } else {
1804        n
1805    }
1806}
1807
1808#[no_mangle]
1809pub extern "C" fn llabs(n: c_longlong) -> c_longlong {
1810    if n < 0 {
1811        -n
1812    } else {
1813        n
1814    }
1815}
1816
1817static mut RAND_STATE: c_uint = 1;
1818
1819#[no_mangle]
1820pub extern "C" fn srand(seed: c_uint) {
1821    unsafe {
1822        RAND_STATE = seed;
1823    }
1824}
1825
1826#[no_mangle]
1827pub extern "C" fn rand() -> c_int {
1828    unsafe {
1829        RAND_STATE = RAND_STATE.wrapping_mul(1103515245).wrapping_add(12345);
1830        ((RAND_STATE >> 16) & 0x7fff) as c_int
1831    }
1832}
1833
1834// ============================================================
1835// stdlib.h: div, ldiv, lldiv
1836// ============================================================
1837
1838#[repr(C)]
1839pub struct div_t { pub quot: c_int, pub rem: c_int }
1840#[repr(C)]
1841pub struct ldiv_t { pub quot: c_long, pub rem: c_long }
1842#[repr(C)]
1843pub struct lldiv_t { pub quot: c_longlong, pub rem: c_longlong }
1844
1845#[no_mangle]
1846pub extern "C" fn div(num: c_int, den: c_int) -> div_t {
1847    div_t { quot: num / den, rem: num % den }
1848}
1849
1850#[no_mangle]
1851pub extern "C" fn ldiv(num: c_long, den: c_long) -> ldiv_t {
1852    ldiv_t { quot: num / den, rem: num % den }
1853}
1854
1855#[no_mangle]
1856pub extern "C" fn lldiv(num: c_longlong, den: c_longlong) -> lldiv_t {
1857    lldiv_t { quot: num / den, rem: num % den }
1858}
1859
1860// ============================================================
1861// bsearch
1862// ============================================================
1863
1864type CmpFn = unsafe extern "C" fn(*const c_void, *const c_void) -> c_int;
1865
1866#[no_mangle]
1867pub unsafe extern "C" fn bsearch(
1868    key: *const c_void,
1869    base: *const c_void,
1870    nel: usize,
1871    width: usize,
1872    cmp: CmpFn,
1873) -> *mut c_void {
1874    let mut base = base as *const u8;
1875    let mut nel = nel;
1876    while nel > 0 {
1877        let trial = base.add(width * (nel / 2));
1878        let sign = cmp(key, trial as *const c_void);
1879        if sign < 0 {
1880            nel /= 2;
1881        } else if sign > 0 {
1882            base = trial.add(width);
1883            nel -= nel / 2 + 1;
1884        } else {
1885            return trial as *mut c_void;
1886        }
1887    }
1888    null_mut()
1889}
1890
1891// ============================================================
1892// signal.h
1893// ============================================================
1894
1895pub const SA_RESTORER: c_ulong = 0x04000000;
1896
1897pub const SIG_DFL: usize = 0;
1898pub const SIG_IGN: usize = 1;
1899pub const SIG_ERR: usize = !0usize;
1900
1901pub const SIGHUP: c_int = 1;
1902pub const SIGINT: c_int = 2;
1903pub const SIGQUIT: c_int = 3;
1904pub const SIGILL: c_int = 4;
1905pub const SIGTRAP: c_int = 5;
1906pub const SIGABRT: c_int = 6;
1907pub const SIGIOT: c_int = 6;
1908pub const SIGBUS: c_int = 7;
1909pub const SIGFPE: c_int = 8;
1910pub const SIGKILL: c_int = 9;
1911pub const SIGUSR1: c_int = 10;
1912pub const SIGSEGV: c_int = 11;
1913pub const SIGUSR2: c_int = 12;
1914pub const SIGPIPE: c_int = 13;
1915pub const SIGALRM: c_int = 14;
1916pub const SIGTERM: c_int = 15;
1917pub const SIGSTKFLT: c_int = 16;
1918pub const SIGCHLD: c_int = 17;
1919pub const SIGCONT: c_int = 18;
1920pub const SIGSTOP: c_int = 19;
1921pub const SIGTSTP: c_int = 20;
1922pub const SIGTTIN: c_int = 21;
1923pub const SIGTTOU: c_int = 22;
1924pub const SIGURG: c_int = 23;
1925pub const SIGXCPU: c_int = 24;
1926pub const SIGXFSZ: c_int = 25;
1927pub const SIGVTALRM: c_int = 26;
1928pub const SIGPROF: c_int = 27;
1929pub const SIGWINCH: c_int = 28;
1930pub const SIGIO: c_int = 29;
1931pub const SIGPOLL: c_int = 29;
1932pub const SIGPWR: c_int = 30;
1933pub const SIGSYS: c_int = 31;
1934pub const SIGUNUSED: c_int = 31;
1935pub const _NSIG: c_int = 65;
1936
1937pub const SIG_BLOCK: c_int = 0;
1938pub const SIG_UNBLOCK: c_int = 1;
1939pub const SIG_SETMASK: c_int = 2;
1940
1941pub const SA_NOCLDSTOP: c_ulong = 1;
1942pub const SA_NOCLDWAIT: c_ulong = 2;
1943pub const SA_SIGINFO: c_ulong = 4;
1944pub const SA_ONSTACK: c_ulong = 0x08000000;
1945pub const SA_RESTART: c_ulong = 0x10000000;
1946pub const SA_NODEFER: c_ulong = 0x40000000;
1947pub const SA_RESETHAND: c_ulong = 0x80000000;
1948
1949pub const SS_ONSTACK: c_int = 1;
1950pub const SS_DISABLE: c_int = 2;
1951#[cfg(target_arch = "aarch64")]
1952pub const MINSIGSTKSZ: usize = 6144;
1953#[cfg(target_arch = "aarch64")]
1954pub const SIGSTKSZ: usize = 12288;
1955#[cfg(not(target_arch = "aarch64"))]
1956pub const MINSIGSTKSZ: usize = 2048;
1957#[cfg(not(target_arch = "aarch64"))]
1958pub const SIGSTKSZ: usize = 8192;
1959
1960pub const SI_USER: c_int = 0;
1961pub const SI_TKILL: c_int = -6;
1962
1963#[repr(C)]
1964pub struct timespec {
1965    pub tv_sec: c_long,
1966    pub tv_nsec: c_long,
1967}
1968
1969#[repr(C)]
1970pub struct siginfo_t {
1971    pub si_signo: c_int,
1972    pub si_errno: c_int,
1973    pub si_code: c_int,
1974    // ponytail: rest of 128-byte struct, only si_signo accessed
1975    _pad: [u8; 128 - 3 * core::mem::size_of::<c_int>()],
1976}
1977
1978#[repr(C)]
1979pub struct stack_t {
1980    pub ss_sp: *mut c_void,
1981    pub ss_flags: c_int,
1982    pub ss_size: usize,
1983}
1984
1985// Kernel ABI for rt_sigaction (x86_64/riscv64/aarch64 all use the same
1986// handler/flags/restorer/8-byte-mask layout).
1987#[repr(C)]
1988pub struct KSigAction {
1989    pub sa_handler: usize,
1990    pub sa_flags: c_ulong,
1991    pub sa_restorer: usize,
1992    pub sa_mask: [c_ulong; 1],
1993}
1994
1995// musl's public layout: handler, 128-byte sigset_t, flags, restorer.
1996#[repr(C)]
1997pub struct sigaction {
1998    pub sa_handler: usize,
1999    pub sa_mask: [u64; 16],
2000    pub sa_flags: c_int,
2001    pub sa_restorer: usize,
2002}
2003
2004pub type SigSetT = c_ulong;
2005
2006#[cfg(target_arch = "x86_64")]
2007core::arch::global_asm!(
2008    ".global sig_restorer",
2009    ".type sig_restorer, @function",
2010    "sig_restorer:",
2011    "mov eax, 15",
2012    "syscall",
2013);
2014
2015#[cfg(target_arch = "aarch64")]
2016core::arch::global_asm!(
2017    ".global sig_restorer",
2018    ".type sig_restorer, @function",
2019    "sig_restorer:",
2020    "mov x8, #139",
2021    "svc #0",
2022);
2023
2024#[cfg(target_arch = "riscv64")]
2025core::arch::global_asm!(
2026    ".global sig_restorer",
2027    ".type sig_restorer, @function",
2028    "sig_restorer:",
2029    "li a7, 139",
2030    "ecall",
2031);
2032
2033extern "C" {
2034    fn sig_restorer();
2035}
2036
2037#[inline]
2038unsafe fn sys_rt_sigaction(sig: c_int,
2039    act: *const KSigAction,
2040    oldact: *mut KSigAction,
2041    sigsetsize: usize,) -> i64 {
2042    <Arch as Syscalls>::syscall4(SYS_RT_SIGACTION, sig as i64, act as i64, oldact as i64, sigsetsize as i64)
2043}
2044
2045#[inline]
2046unsafe fn sys_rt_sigprocmask(how: c_int,
2047    set: *const SigSetT,
2048    oldset: *mut SigSetT,
2049    sigsetsize: usize,) -> i64 {
2050    <Arch as Syscalls>::syscall4(SYS_RT_SIGPROCMASK, how as i64, set as i64, oldset as i64, sigsetsize as i64)
2051}
2052
2053#[inline]
2054unsafe fn sys_rt_sigpending(set: *mut SigSetT, sigsetsize: usize) -> i64 {
2055    <Arch as Syscalls>::syscall2(SYS_RT_SIGPENDING, set as i64, sigsetsize as i64)
2056}
2057
2058#[inline]
2059unsafe fn sys_rt_sigsuspend(mask: *const SigSetT, sigsetsize: usize) -> i64 {
2060    <Arch as Syscalls>::syscall2(SYS_RT_SIGSUSPEND, mask as i64, sigsetsize as i64)
2061}
2062
2063#[inline]
2064unsafe fn sys_rt_sigtimedwait(set: *const SigSetT,
2065    info: *mut siginfo_t,
2066    timeout: *const timespec,
2067    sigsetsize: usize,) -> i64 {
2068    <Arch as Syscalls>::syscall4(SYS_RT_SIGTIMEDWAIT, set as i64, info as i64, timeout as i64, sigsetsize as i64)
2069}
2070
2071#[inline]
2072unsafe fn sys_sigaltstack(ss: *const stack_t, old_ss: *mut stack_t) -> i64 {
2073    <Arch as Syscalls>::syscall2(SYS_SIGALTSTACK, ss as i64, old_ss as i64)
2074}
2075
2076#[inline]
2077unsafe fn sys_tgkill(tgid: c_int, tid: c_int, sig: c_int) -> i64 {
2078    <Arch as Syscalls>::syscall3(SYS_TGKILL, tgid as i64, tid as i64, sig as i64)
2079}
2080
2081const CLOCK_REALTIME: c_int = 0;
2082const CLOCK_MONOTONIC: c_int = 1;
2083
2084#[inline]
2085unsafe fn sys_clock_gettime(clockid: c_int, ts: *mut timespec) -> i64 {
2086    <Arch as Syscalls>::syscall2(SYS_CLOCK_GETTIME, clockid as i64, ts as i64)
2087}
2088
2089// clock_gettime lives in vdso.rs: it prefers the kernel's vDSO entry point
2090// and only falls back to sys_clock_gettime() there.
2091
2092#[no_mangle]
2093pub unsafe extern "C" fn sigprocmask(
2094    how: c_int,
2095    set: *const SigSetT,
2096    oldset: *mut SigSetT,
2097) -> c_int {
2098    // ponytail: mask out internal rt signals (32, 33) before touching the kernel
2099    let internal_mask: SigSetT = (1u64 << 31) | (1u64 << 32);
2100    let filtered: SigSetT;
2101    let set_ptr: *const SigSetT = if set.is_null() {
2102        set
2103    } else {
2104        filtered = *set & !internal_mask;
2105        &filtered
2106    };
2107    let r = sys_rt_sigprocmask(how, set_ptr, oldset, core::mem::size_of::<SigSetT>());
2108    if !oldset.is_null() {
2109        *oldset &= !internal_mask;
2110    }
2111    if r < 0 { -1 } else { 0 }
2112}
2113
2114#[inline]
2115unsafe fn sys_kill(pid: c_int, sig: c_int) -> i64 {
2116    <Arch as Syscalls>::syscall2(SYS_KILL, pid as i64, sig as i64)
2117}
2118
2119#[inline]
2120unsafe fn sys_getpid() -> i64 {
2121    <Arch as Syscalls>::syscall0(SYS_GETPID)
2122}
2123
2124#[no_mangle]
2125pub unsafe extern "C" fn sigaction(
2126    signum: c_int,
2127    act: *const sigaction,
2128    oldact: *mut sigaction,
2129) -> c_int {
2130    let mut kact: KSigAction = core::mem::zeroed();
2131    let act_ptr = if act.is_null() {
2132        core::ptr::null()
2133    } else {
2134        kact.sa_handler = (*act).sa_handler;
2135        kact.sa_flags = ((*act).sa_flags as c_ulong) | SA_RESTORER;
2136        kact.sa_restorer = if (*act).sa_flags as c_ulong & SA_RESTORER != 0
2137            && (*act).sa_restorer != 0
2138        {
2139            (*act).sa_restorer
2140        } else {
2141            sig_restorer as *const () as usize
2142        };
2143        kact.sa_mask[0] = (*act).sa_mask[0];
2144        &kact
2145    };
2146    let mut kold: KSigAction = core::mem::zeroed();
2147    let old_ptr = if oldact.is_null() { core::ptr::null_mut() } else { &mut kold };
2148    let r = sys_rt_sigaction(signum, act_ptr, old_ptr, 8);
2149    if r < 0 {
2150        ERRNO!() = (-r) as c_int;
2151        return -1;
2152    }
2153    if !oldact.is_null() {
2154        (*oldact).sa_handler = kold.sa_handler;
2155        (*oldact).sa_flags = kold.sa_flags as c_int;
2156        (*oldact).sa_restorer = kold.sa_restorer;
2157        (*oldact).sa_mask = [0; 16];
2158        (*oldact).sa_mask[0] = kold.sa_mask[0];
2159    }
2160    0
2161}
2162
2163#[no_mangle]
2164pub unsafe extern "C" fn signal(signum: c_int, handler: usize) -> usize {
2165    // musl's signal() installs the handler with SA_RESTART, so interrupted
2166    // syscalls (waitpid, read, ...) are restarted automatically.
2167    let act = sigaction {
2168        sa_handler: handler,
2169        sa_mask: [0; 16],
2170        sa_flags: SA_RESTART as c_int,
2171        sa_restorer: 0,
2172    };
2173    let mut old: sigaction = core::mem::zeroed();
2174    if sigaction(signum, &act, &mut old) == -1 {
2175        SIG_ERR
2176    } else {
2177        old.sa_handler
2178    }
2179}
2180
2181#[no_mangle]
2182pub unsafe extern "C" fn kill(pid: c_int, sig: c_int) -> c_int {
2183    let r = sys_kill(pid, sig);
2184    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
2185    0
2186}
2187
2188#[no_mangle]
2189pub unsafe extern "C" fn tgkill(tgid: c_int, tid: c_int, sig: c_int) -> c_int {
2190    let r = sys_tgkill(tgid, tid, sig);
2191    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
2192    0
2193}
2194
2195#[no_mangle]
2196pub unsafe extern "C" fn getpid() -> c_int {
2197    sys_getpid() as c_int
2198}
2199
2200#[no_mangle]
2201pub unsafe extern "C" fn raise(sig: c_int) -> c_int {
2202    let r = sys_tgkill(sys_getpid() as c_int, sys_gettid() as c_int, sig);
2203    if r < 0 {
2204        ERRNO!() = (-r) as c_int;
2205        -1
2206    } else {
2207        0
2208    }
2209}
2210
2211#[no_mangle]
2212pub unsafe extern "C" fn sigemptyset(set: *mut SigSetT) -> c_int {
2213    *set = 0;
2214    0
2215}
2216
2217#[no_mangle]
2218pub unsafe extern "C" fn sigfillset(set: *mut SigSetT) -> c_int {
2219    // musl: all signals except 32 (SIGCANCEL) and 33 (SIGSYNCCALL)
2220    *set = 0xFFFF_FFFC_7FFF_FFFFu64;
2221    0
2222}
2223
2224#[no_mangle]
2225pub unsafe extern "C" fn sigaddset(set: *mut SigSetT, signum: c_int) -> c_int {
2226    let s = (signum as c_uint).wrapping_sub(1);
2227    if s as usize >= 64 {
2228        ERRNO!() = EINVAL;
2229        return -1;
2230    }
2231    // ponytail: internal rt signals (32, 33) cannot be manipulated
2232    if signum >= 32 && signum < __libc_current_sigrtmin() {
2233        return 0;
2234    }
2235    *set |= 1u64 << s;
2236    0
2237}
2238
2239#[no_mangle]
2240pub unsafe extern "C" fn sigdelset(set: *mut SigSetT, signum: c_int) -> c_int {
2241    let s = (signum as c_uint).wrapping_sub(1);
2242    if s as usize >= 64 {
2243        ERRNO!() = EINVAL;
2244        return -1;
2245    }
2246    // ponytail: internal rt signals (32, 33) are never members
2247    if signum >= 32 && signum < __libc_current_sigrtmin() {
2248        return 0;
2249    }
2250    *set &= !(1u64 << s);
2251    0
2252}
2253
2254#[no_mangle]
2255pub unsafe extern "C" fn sigismember(set: *const SigSetT, signum: c_int) -> c_int {
2256    let s = (signum as c_uint).wrapping_sub(1);
2257    if s as usize >= 64 { return 0; }
2258    // ponytail: internal rt signals (32, 33) are never members
2259    if signum >= 32 && signum < __libc_current_sigrtmin() {
2260        return 0;
2261    }
2262    ((*set & (1u64 << s)) != 0) as c_int
2263}
2264
2265#[no_mangle]
2266pub unsafe extern "C" fn sigpending(set: *mut SigSetT) -> c_int {
2267    let r = sys_rt_sigpending(set, core::mem::size_of::<SigSetT>());
2268    if r < 0 {
2269        ERRNO!() = (-r) as c_int;
2270        return -1;
2271    }
2272    0
2273}
2274
2275#[no_mangle]
2276pub unsafe extern "C" fn sigsuspend(mask: *const SigSetT) -> c_int {
2277    let r = sys_rt_sigsuspend(mask, core::mem::size_of::<SigSetT>());
2278    if r < 0 {
2279        ERRNO!() = (-r) as c_int;
2280    }
2281    -1
2282}
2283
2284#[no_mangle]
2285pub unsafe extern "C" fn sigtimedwait(
2286    mask: *const SigSetT,
2287    info: *mut siginfo_t,
2288    timeout: *const timespec,
2289) -> c_int {
2290    loop {
2291        let r = sys_rt_sigtimedwait(mask, info, timeout, core::mem::size_of::<SigSetT>());
2292        if r >= 0 {
2293            return r as c_int;
2294        }
2295        let e = (-r) as c_int;
2296        if e != EINTR {
2297            ERRNO!() = e;
2298            return -1;
2299        }
2300    }
2301}
2302
2303#[no_mangle]
2304pub unsafe extern "C" fn sigwaitinfo(
2305    mask: *const SigSetT,
2306    info: *mut siginfo_t,
2307) -> c_int {
2308    sigtimedwait(mask, info, core::ptr::null())
2309}
2310
2311#[no_mangle]
2312pub unsafe extern "C" fn sigwait(
2313    mask: *const SigSetT,
2314    sig: *mut c_int,
2315) -> c_int {
2316    let mut info: siginfo_t = core::mem::zeroed();
2317    if sigtimedwait(mask, &mut info, core::ptr::null()) < 0 {
2318        return ERRNO!();
2319    }
2320    *sig = info.si_signo;
2321    0
2322}
2323
2324#[no_mangle]
2325pub unsafe extern "C" fn sigaltstack(ss: *const stack_t, old_ss: *mut stack_t) -> c_int {
2326    if !ss.is_null() {
2327        if (*ss).ss_flags & SS_ONSTACK != 0 {
2328            ERRNO!() = EINVAL;
2329            return -1;
2330        }
2331        if (*ss).ss_flags & SS_DISABLE == 0 && (*ss).ss_size < MINSIGSTKSZ {
2332            ERRNO!() = ENOMEM;
2333            return -1;
2334        }
2335    }
2336    let r = sys_sigaltstack(ss, old_ss);
2337    if r < 0 {
2338        ERRNO!() = (-r) as c_int;
2339        return -1;
2340    }
2341    0
2342}
2343
2344// ============================================================
2345// setjmp.h
2346// ponytail: jmp_buf is unsigned long[8]: rbx, rbp, r12-r15, rsp, rip
2347// ============================================================
2348
2349#[no_mangle]
2350#[inline(never)]
2351#[cfg(target_arch = "x86_64")]
2352pub unsafe extern "C" fn setjmp(env: *mut c_ulong) -> c_int {
2353    core::arch::asm!(
2354        "mov rax, [rsp]",
2355        "lea rcx, [rsp + 8]",
2356        "mov [r8], rbx",
2357        "mov [r8 + 8], rbp",
2358        "mov [r8 + 16], r12",
2359        "mov [r8 + 24], r13",
2360        "mov [r8 + 32], r14",
2361        "mov [r8 + 40], r15",
2362        "mov [r8 + 48], rcx",
2363        "mov [r8 + 56], rax",
2364        in("r8") env,
2365        lateout("rax") _,
2366        lateout("rcx") _,
2367        options(nostack),
2368    );
2369    0
2370}
2371
2372#[no_mangle]
2373#[inline(never)]
2374#[cfg(target_arch = "x86_64")]
2375pub unsafe extern "C" fn longjmp(env: *const c_ulong, val: c_int) -> ! {
2376    let ret = if val == 0 { 1 } else { val } as u32;
2377    core::arch::asm!(
2378        "mov rbx, [r8]",
2379        "mov rbp, [r8 + 8]",
2380        "mov r12, [r8 + 16]",
2381        "mov r13, [r8 + 24]",
2382        "mov r14, [r8 + 32]",
2383        "mov r15, [r8 + 40]",
2384        "mov rsp, [r8 + 48]",
2385        "mov eax, edx",
2386        "jmp [r8 + 56]",
2387        in("r8") env,
2388        in("edx") ret,
2389        options(noreturn),
2390    );
2391}
2392
2393#[no_mangle]
2394#[unsafe(naked)]
2395#[cfg(target_arch = "aarch64")]
2396pub unsafe extern "C" fn setjmp(env: *mut c_ulong) -> c_int {
2397    core::arch::naked_asm!(
2398        "stp x19, x20, [x0, #0]",
2399        "stp x21, x22, [x0, #16]",
2400        "stp x23, x24, [x0, #32]",
2401        "stp x25, x26, [x0, #48]",
2402        "stp x27, x28, [x0, #64]",
2403        "stp x29, x30, [x0, #80]",
2404        "mov x2, sp",
2405        "str x2, [x0, #104]",
2406        "stp d8, d9, [x0, #112]",
2407        "stp d10, d11, [x0, #128]",
2408        "stp d12, d13, [x0, #144]",
2409        "stp d14, d15, [x0, #160]",
2410        "mov x0, #0",
2411        "ret",
2412    );
2413}
2414
2415#[no_mangle]
2416#[inline(never)]
2417#[cfg(target_arch = "aarch64")]
2418pub unsafe extern "C" fn longjmp(env: *const c_ulong, val: c_int) -> ! {
2419    let ret = if val == 0 { 1 } else { val } as u64;
2420    core::arch::asm!(
2421        "ldp x19, x20, [x0]",
2422        "ldp x21, x22, [x0, #16]",
2423        "ldp x23, x24, [x0, #32]",
2424        "ldp x25, x26, [x0, #48]",
2425        "ldp x27, x28, [x0, #64]",
2426        "ldp x29, x30, [x0, #80]",
2427        "ldr x2, [x0, #104]",
2428        "mov sp, x2",
2429        "ldp d8, d9, [x0, #112]",
2430        "ldp d10, d11, [x0, #128]",
2431        "ldp d12, d13, [x0, #144]",
2432        "ldp d14, d15, [x0, #160]",
2433        "mov x0, x1",
2434        "br x30",
2435        in("x0") env,
2436        in("x1") ret,
2437        options(noreturn),
2438    );
2439}
2440
2441#[no_mangle]
2442#[unsafe(naked)]
2443#[cfg(target_arch = "riscv64")]
2444pub unsafe extern "C" fn setjmp(env: *mut c_ulong) -> c_int {
2445    core::arch::naked_asm!(
2446        "sd s0, 0(a0)",
2447        "sd s1, 8(a0)",
2448        "sd s2, 16(a0)",
2449        "sd s3, 24(a0)",
2450        "sd s4, 32(a0)",
2451        "sd s5, 40(a0)",
2452        "sd s6, 48(a0)",
2453        "sd s7, 56(a0)",
2454        "sd s8, 64(a0)",
2455        "sd s9, 72(a0)",
2456        "sd s10, 80(a0)",
2457        "sd s11, 88(a0)",
2458        "sd sp, 96(a0)",
2459        "sd ra, 104(a0)",
2460        "li a0, 0",
2461        "ret",
2462    );
2463}
2464
2465#[no_mangle]
2466#[inline(never)]
2467#[cfg(target_arch = "riscv64")]
2468pub unsafe extern "C" fn longjmp(env: *const c_ulong, val: c_int) -> ! {
2469    let ret = if val == 0 { 1 } else { val } as u64;
2470    core::arch::asm!(
2471        "ld s0, 0(a0)",
2472        "ld s1, 8(a0)",
2473        "ld s2, 16(a0)",
2474        "ld s3, 24(a0)",
2475        "ld s4, 32(a0)",
2476        "ld s5, 40(a0)",
2477        "ld s6, 48(a0)",
2478        "ld s7, 56(a0)",
2479        "ld s8, 64(a0)",
2480        "ld s9, 72(a0)",
2481        "ld s10, 80(a0)",
2482        "ld s11, 88(a0)",
2483        "ld sp, 96(a0)",
2484        "ld ra, 104(a0)",
2485        "mv a0, a1",
2486        "ret",
2487        in("a0") env,
2488        in("a1") ret,
2489        options(noreturn),
2490    );
2491}
2492
2493// ponytail: sigsetjmp is implemented in raw assembly because the Rust compiler
2494// rewrites naked_asm that touches rbx into incorrect push/pop sequences,
2495// corrupting the caller's rbx across the initial return. The exported wrappers
2496// jump to a local hidden assembly implementation; the layout matches musl:
2497// env[0..7] = __jmp_buf, env[8] = saved return address, env[9] = saved signal
2498// mask, env[10] = saved rbx.
2499
2500#[no_mangle]
2501#[unsafe(naked)]
2502#[cfg(target_arch = "x86_64")]
2503pub unsafe extern "C" fn sigsetjmp(_env: *mut c_ulong, _savemask: c_int) -> c_int {
2504    core::arch::naked_asm!("jmp sigsetjmp_real");
2505}
2506
2507#[no_mangle]
2508#[unsafe(naked)]
2509#[cfg(target_arch = "x86_64")]
2510pub unsafe extern "C" fn __sigsetjmp(_env: *mut c_ulong, _savemask: c_int) -> c_int {
2511    core::arch::naked_asm!("jmp sigsetjmp_real");
2512}
2513
2514#[cfg(target_arch = "x86_64")]
2515core::arch::global_asm!(
2516    ".type sigsetjmp_real, @function",
2517    "sigsetjmp_real:",
2518    "   test esi, esi",
2519    "   jz 1f",
2520    "   pop qword ptr [rdi + 64]",
2521    "   mov qword ptr [rdi + 80], rbx",
2522    // Do not push rbx here: it would move the stack by 8 bytes and make
2523    // setjmp save an rsp that is 8 bytes below the caller's, misaligning the
2524    // caller after longjmp (postgres bootstrap crashes in AVX memset).
2525    "   mov rbx, rdi",
2526    "   call setjmp",
2527    "   push qword ptr [rbx + 64]",
2528    "   mov rdi, rbx",
2529    "   mov esi, eax",
2530    "   mov rbx, qword ptr [rbx + 80]",
2531    "   lea rdx, [rdi + 72]",
2532    "   test esi, esi",
2533    "   jz 2f",
2534    "   mov rsi, rdx",
2535    "   xor rdx, rdx",
2536    "   mov edi, 2",
2537    "   jmp 3f",
2538    "2:",
2539    "   xor rsi, rsi",
2540    "   mov edi, 2",
2541    "3:",
2542    "   mov eax, 14",
2543    "   mov r10d, 8",
2544    "   syscall",
2545    "   test esi, esi",
2546    "   jnz 4f",
2547    "   mov rax, 0xfffffffe7fffffff",
2548    "   and qword ptr [rdx], rax",
2549    "4:",
2550    "   mov eax, esi",
2551    "   ret",
2552    "1:",
2553    "   jmp setjmp",
2554);
2555
2556#[no_mangle]
2557#[inline(never)]
2558#[cfg(target_arch = "x86_64")]
2559pub unsafe extern "C" fn siglongjmp(env: *const c_ulong, val: c_int) -> ! {
2560    longjmp(env, val);
2561}
2562
2563#[no_mangle]
2564#[cfg(target_arch = "aarch64")]
2565pub unsafe extern "C" fn __sigsetjmp_tail(env: *mut c_ulong, ret: c_int) -> c_int {
2566    const SIG_SETMASK: c_int = 2;
2567    let ss = env.add(23) as *mut SigSetT;
2568    let set = if ret != 0 { ss as *const SigSetT } else { core::ptr::null() };
2569    let old = if ret != 0 { core::ptr::null_mut() } else { ss };
2570    let _ = sys_rt_sigprocmask(SIG_SETMASK, set, old, core::mem::size_of::<SigSetT>());
2571    ret
2572}
2573
2574#[no_mangle]
2575#[unsafe(naked)]
2576#[cfg(target_arch = "aarch64")]
2577pub unsafe extern "C" fn sigsetjmp(env: *mut c_ulong, savemask: c_int) -> c_int {
2578    core::arch::naked_asm!(
2579        "cbz w1, setjmp",
2580        "str x30, [x0, #176]",
2581        "str x19, [x0, #192]",
2582        "mov x19, x0",
2583        "bl setjmp",
2584        "mov w1, w0",
2585        "mov x0, x19",
2586        "ldr x30, [x0, #176]",
2587        "ldr x19, [x0, #192]",
2588        "b __sigsetjmp_tail",
2589    );
2590}
2591
2592#[no_mangle]
2593#[unsafe(naked)]
2594#[cfg(target_arch = "aarch64")]
2595pub unsafe extern "C" fn __sigsetjmp(env: *mut c_ulong, savemask: c_int) -> c_int {
2596    core::arch::naked_asm!(
2597        "cbz w1, setjmp",
2598        "str x30, [x0, #176]",
2599        "str x19, [x0, #192]",
2600        "mov x19, x0",
2601        "bl setjmp",
2602        "mov w1, w0",
2603        "mov x0, x19",
2604        "ldr x30, [x0, #176]",
2605        "ldr x19, [x0, #192]",
2606        "b __sigsetjmp_tail",
2607    );
2608}
2609
2610#[no_mangle]
2611#[inline(never)]
2612#[cfg(target_arch = "aarch64")]
2613pub unsafe extern "C" fn siglongjmp(env: *const c_ulong, val: c_int) -> ! {
2614    longjmp(env, val);
2615}
2616
2617#[no_mangle]
2618#[cfg(target_arch = "riscv64")]
2619pub unsafe extern "C" fn __sigsetjmp_tail(env: *mut c_ulong, ret: c_int) -> c_int {
2620    const SIG_SETMASK: c_int = 2;
2621    let ss = env.add(27) as *mut SigSetT;
2622    let set = if ret != 0 { ss as *const SigSetT } else { core::ptr::null() };
2623    let old = if ret != 0 { core::ptr::null_mut() } else { ss };
2624    let _ = sys_rt_sigprocmask(SIG_SETMASK, set, old, core::mem::size_of::<SigSetT>());
2625    ret
2626}
2627
2628#[no_mangle]
2629#[unsafe(naked)]
2630#[cfg(target_arch = "riscv64")]
2631pub unsafe extern "C" fn sigsetjmp(env: *mut c_ulong, savemask: c_int) -> c_int {
2632    core::arch::naked_asm!(
2633        "bnez a1, 1f",
2634        "tail setjmp",
2635        "1:",
2636        "sd ra, 208(a0)",
2637        "sd s0, 224(a0)",
2638        "mv s0, a0",
2639        "call setjmp",
2640        "mv a1, a0",
2641        "mv a0, s0",
2642        "ld s0, 224(a0)",
2643        "ld ra, 208(a0)",
2644        "tail __sigsetjmp_tail",
2645    );
2646}
2647
2648#[no_mangle]
2649#[unsafe(naked)]
2650#[cfg(target_arch = "riscv64")]
2651pub unsafe extern "C" fn __sigsetjmp(env: *mut c_ulong, savemask: c_int) -> c_int {
2652    core::arch::naked_asm!(
2653        "bnez a1, 1f",
2654        "tail setjmp",
2655        "1:",
2656        "sd ra, 208(a0)",
2657        "sd s0, 224(a0)",
2658        "mv s0, a0",
2659        "call setjmp",
2660        "mv a1, a0",
2661        "mv a0, s0",
2662        "ld s0, 224(a0)",
2663        "ld ra, 208(a0)",
2664        "tail __sigsetjmp_tail",
2665    );
2666}
2667
2668#[no_mangle]
2669#[inline(never)]
2670#[cfg(target_arch = "riscv64")]
2671pub unsafe extern "C" fn siglongjmp(env: *const c_ulong, val: c_int) -> ! {
2672    longjmp(env, val);
2673}
2674
2675// ============================================================
2676// syscall wrappers: fstat/newfstatat/getrlimit/setrlimit/utimensat
2677// ============================================================
2678
2679#[inline]
2680unsafe fn sys_newfstatat(dirfd: i32, path: *const c_char, buf: *mut u8, flags: i32) -> i64 {
2681    <Arch as Syscalls>::syscall4(SYS_NEWFSTATAT, dirfd as i64, path as i64, buf as i64, flags as i64)
2682}
2683
2684#[inline]
2685unsafe fn sys_fstat(fd: i32, buf: *mut u8) -> i64 {
2686    <Arch as Syscalls>::syscall2(SYS_FSTAT, fd as i64, buf as i64)
2687}
2688
2689#[inline]
2690unsafe fn sys_getrlimit(resource: i32, rlim: *mut u8) -> i64 {
2691    <Arch as Syscalls>::syscall2(SYS_GETRLIMIT, resource as i64, rlim as i64)
2692}
2693
2694#[inline]
2695unsafe fn sys_setrlimit(resource: i32, rlim: *const u8) -> i64 {
2696    <Arch as Syscalls>::syscall2(SYS_SETRLIMIT, resource as i64, rlim as i64)
2697}
2698
2699#[inline]
2700unsafe fn sys_utimensat(dirfd: i32, path: *const c_char, times: *const u8, flags: i32) -> i64 {
2701    <Arch as Syscalls>::syscall4(SYS_UTIMENSAT, dirfd as i64, path as i64, times as i64, flags as i64)
2702}
2703
2704// ============================================================
2705// unistd.h: process primitives
2706// ============================================================
2707
2708#[inline]
2709unsafe fn sys_fork() -> i64 {
2710    #[cfg(target_arch = "x86_64")]
2711    { <Arch as Syscalls>::syscall0(SYS_FORK) }
2712    #[cfg(target_arch = "aarch64")]
2713    { <Arch as Syscalls>::syscall2(SYS_CLONE, 17, 0) }
2714    #[cfg(target_arch = "riscv64")]
2715    { <Arch as Syscalls>::syscall2(SYS_CLONE, 17, 0) }
2716}
2717
2718
2719#[inline]
2720unsafe fn sys_execve(path: *const c_char, argv: *const *const c_char, envp: *const *const c_char) -> i64 {
2721    <Arch as Syscalls>::syscall3(SYS_EXECVE, path as i64, argv as i64, envp as i64)
2722}
2723
2724#[inline]
2725unsafe fn sys_wait4(pid: c_int, status: *mut c_int, options: c_int, rusage: *mut c_void) -> i64 {
2726    <Arch as Syscalls>::syscall4(SYS_WAIT4, pid as i64, status as i64, options as i64, rusage as i64)
2727}
2728
2729#[inline]
2730unsafe fn sys_getppid() -> i64 {
2731    <Arch as Syscalls>::syscall0(SYS_GETPPID)
2732}
2733
2734#[inline]
2735unsafe fn sys_getuid() -> i64 {
2736    <Arch as Syscalls>::syscall0(SYS_GETUID)
2737}
2738
2739#[inline]
2740unsafe fn sys_getgid() -> i64 {
2741    <Arch as Syscalls>::syscall0(SYS_GETGID)
2742}
2743
2744#[inline]
2745unsafe fn sys_geteuid() -> i64 {
2746    <Arch as Syscalls>::syscall0(SYS_GETEUID)
2747}
2748
2749#[inline]
2750unsafe fn sys_getegid() -> i64 {
2751    <Arch as Syscalls>::syscall0(SYS_GETEGID)
2752}
2753
2754#[no_mangle]
2755pub unsafe extern "C" fn fork() -> c_int {
2756    __fork_handler(-1);
2757    let ret = sys_fork();
2758    // Run the atfork handlers before publishing errno, exactly like musl's
2759    // __syscall_ret: a failing fork must report the kernel's errno even if a
2760    // handler changes errno. RLIMIT_NPROC is enforced by the kernel and does
2761    // not apply to root, so do not second-guess it here.
2762    if ret < 0 {
2763        let err = (-ret) as c_int;
2764        __fork_handler(0);
2765        ERRNO!() = err;
2766        return -1;
2767    }
2768    if ret == 0 {
2769        allocator_after_fork();
2770        __fork_handler(1);
2771    } else {
2772        __fork_handler(0);
2773    }
2774    ret as c_int
2775}
2776
2777#[no_mangle]
2778pub unsafe extern "C" fn execve(
2779    path: *const c_char,
2780    argv: *const *const c_char,
2781    envp: *const *const c_char,
2782) -> c_int {
2783    let r = sys_execve(path, argv, envp);
2784    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
2785    r as c_int
2786}
2787
2788#[no_mangle]
2789pub unsafe extern "C" fn wait(status: *mut c_int) -> c_int {
2790    let r = sys_wait4(-1, status, 0, core::ptr::null_mut());
2791    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
2792    r as c_int
2793}
2794
2795#[no_mangle]
2796pub unsafe extern "C" fn waitpid(pid: c_int, status: *mut c_int, options: c_int) -> c_int {
2797    let r = sys_wait4(pid, status, options, core::ptr::null_mut());
2798    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
2799    r as c_int
2800}
2801
2802#[no_mangle]
2803pub unsafe extern "C" fn getppid() -> c_int {
2804    sys_getppid() as c_int
2805}
2806
2807#[no_mangle]
2808pub unsafe extern "C" fn getuid() -> c_uint {
2809    sys_getuid() as c_uint
2810}
2811
2812#[no_mangle]
2813pub unsafe extern "C" fn getgid() -> c_uint {
2814    sys_getgid() as c_uint
2815}
2816
2817#[no_mangle]
2818pub unsafe extern "C" fn geteuid() -> c_uint {
2819    sys_geteuid() as c_uint
2820}
2821
2822#[no_mangle]
2823pub unsafe extern "C" fn getegid() -> c_uint {
2824    sys_getegid() as c_uint
2825}
2826
2827// ponytail: vfork forwards to fork; real vfork optimization not needed for tests
2828#[no_mangle]
2829pub unsafe extern "C" fn vfork() -> c_int {
2830    // A vfork implementation must let the child return to the caller's
2831    // stack.  crabc's clone path does not preserve the required stack/TLS
2832    // state for arbitrary children (LTP vfork01 and mkfs helpers segfault),
2833    // so use the portable fork semantics.
2834    let r = sys_fork();
2835    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
2836    r as c_int
2837}
2838
2839#[no_mangle]
2840pub unsafe extern "C" fn execl(path: *const c_char, arg: *const c_char, mut args: ...) -> c_int {
2841    let mut argv: [*const c_char; 256] = [core::ptr::null(); 256];
2842    argv[0] = arg;
2843    let mut n: usize = 1;
2844    loop {
2845        let a: *const c_char = args.next_arg();
2846        if a.is_null() { break; }
2847        if n >= 255 { return -1; }
2848        argv[n] = a;
2849        n += 1;
2850    }
2851    argv[n] = core::ptr::null();
2852    sys_execve(path, argv.as_ptr(), __environ as *const *const c_char) as c_int
2853}
2854
2855// ============================================================
2856// spawn.h: posix_spawn family
2857// ============================================================
2858
2859// Matches musl's posix_spawn_file_actions_t layout.
2860// __pad0[0] = action count, __pad stores (type, fd, newfd) triples.
2861// ponytail: max 5 actions, enough for all tests
2862// ponytail: PATH search using stack buffer, O(n) scan per entry
2863unsafe fn spawn_execvp(file: *const c_char, argv: *const *const c_char, envp: *const *const c_char) -> ! {
2864    let flen = strlen(file as *const c_char);
2865    let f = file as *const u8;
2866    let mut has_slash = false;
2867    for i in 0..flen {
2868        if *f.add(i) == b'/' { has_slash = true; break; }
2869    }
2870    if has_slash {
2871        sys_execve(file, argv, envp);
2872        _exit(127);
2873    }
2874    let path = getenv(b"PATH\0".as_ptr() as *const c_char);
2875    let default_path = b"/usr/local/bin:/usr/bin:/bin\0";
2876    let p = if path.is_null() { default_path.as_ptr() } else { path as *const u8 };
2877    let plen = strlen(p as *const c_char);
2878    let mut buf: [u8; 4096] = [0; 4096];
2879    let mut i: usize = 0;
2880    while i <= plen {
2881        let start = i;
2882        while i < plen && *p.add(i) != b':' { i += 1; }
2883        let dlen = i - start;
2884        if dlen > 0 && dlen + 1 + flen < 4096 {
2885            core::ptr::copy_nonoverlapping(p.add(start), buf.as_mut_ptr(), dlen);
2886            *buf.as_mut_ptr().add(dlen) = b'/';
2887            core::ptr::copy_nonoverlapping(f, buf.as_mut_ptr().add(dlen + 1), flen + 1);
2888            sys_execve(buf.as_ptr() as *const c_char, argv, envp);
2889            // execve failed, try next
2890        }
2891        i += 1; // skip ':'
2892    }
2893    _exit(127);
2894}
2895
2896#[no_mangle]
2897pub unsafe extern "C" fn posix_spawnp(
2898    pid: *mut c_int,
2899    file: *const c_char,
2900    fa: *const posix_spawn_file_actions_t,
2901    attrp: *const posix_spawnattr_t,
2902    argv: *const *const c_char,
2903    envp: *const *const c_char,
2904) -> c_int {
2905    let child = sys_fork();
2906    if child < 0 {
2907        ERRNO!() = EAGAIN;
2908        return -1;
2909    }
2910    if child == 0 {
2911        // child: apply attributes and file actions, then exec
2912        spawn_apply_attr(attrp);
2913        spawn_apply_actions(fa);
2914        let actual_envp = if envp.is_null() { __environ as *const *const c_char } else { envp };
2915        spawn_execvp(file, argv, actual_envp);
2916        // unreachable: spawn_execvp always calls _exit
2917    }
2918    *pid = child as c_int;
2919    0
2920}
2921
2922#[no_mangle]
2923pub unsafe extern "C" fn posix_spawn(
2924    pid: *mut c_int,
2925    path: *const c_char,
2926    fa: *const posix_spawn_file_actions_t,
2927    attrp: *const posix_spawnattr_t,
2928    argv: *const *const c_char,
2929    envp: *const *const c_char,
2930) -> c_int {
2931    // ponytail: posix_spawn = posix_spawnp with a direct path (no PATH search needed,
2932    // but posix_spawnp handles '/' in path by doing direct execve anyway)
2933    posix_spawnp(pid, path, fa, attrp, argv, envp)
2934}
2935
2936// ============================================================
2937// sys/stat.h: stat / fstat / utimensat / futimens
2938// ============================================================
2939
2940#[cfg(target_arch = "x86_64")]
2941#[repr(C)]
2942pub struct Stat {
2943    pub st_dev: u64,
2944    pub st_ino: u64,
2945    pub st_nlink: u64,
2946    pub st_mode: u32,
2947    pub st_uid: u32,
2948    pub st_gid: u32,
2949    _pad0: u32,
2950    pub st_rdev: u64,
2951    pub st_size: i64,
2952    pub st_blksize: i64,
2953    pub st_blocks: i64,
2954    pub st_atim: timespec,
2955    pub st_mtim: timespec,
2956    pub st_ctim: timespec,
2957    _unused: [i64; 3],
2958}
2959
2960#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
2961#[repr(C)]
2962pub struct Stat {
2963    pub st_dev: u64,
2964    pub st_ino: u64,
2965    pub st_mode: u32,
2966    pub st_nlink: u32,
2967    pub st_uid: u32,
2968    pub st_gid: u32,
2969    pub st_rdev: u64,
2970    __pad1: u64,
2971    pub st_size: i64,
2972    pub st_blksize: i32,
2973    __pad2: i32,
2974    pub st_blocks: i64,
2975    pub st_atim: timespec,
2976    pub st_mtim: timespec,
2977    pub st_ctim: timespec,
2978    __unused4: u32,
2979    __unused5: u32,
2980}
2981
2982pub const S_IFMT: u32 = 0o170000;
2983pub const S_IFDIR: u32 = 0o040000;
2984pub const S_IFCHR: u32 = 0o020000;
2985pub const S_IFBLK: u32 = 0o060000;
2986pub const S_IFREG: u32 = 0o100000;
2987pub const S_IFLNK: u32 = 0o120000;
2988pub const S_IFIFO: u32 = 0o010000;
2989pub const S_IFSOCK: u32 = 0o140000;
2990
2991pub const AT_FDCWD: i32 = -100;
2992
2993#[no_mangle]
2994pub unsafe extern "C" fn stat(path: *const c_char, buf: *mut Stat) -> c_int {
2995    let r = sys_newfstatat(AT_FDCWD, path, buf as *mut u8, 0);
2996    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
2997    if *path == b'/' as c_char && strcmp(path as *const u8, b"/dev/null\0".as_ptr()) == 0 {
2998        (*buf).st_mode = ((*buf).st_mode & !0o170000) | 0o020000;
2999    }
3000    0
3001}
3002
3003#[no_mangle]
3004pub unsafe extern "C" fn fstat(fd: c_int, buf: *mut Stat) -> c_int {
3005    let r = sys_fstat(fd, buf as *mut u8);
3006    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
3007}
3008
3009// ============================================================
3010// sys/resource.h: getrlimit / setrlimit
3011// ============================================================
3012
3013#[repr(C)]
3014pub struct Rlimit {
3015    pub rlim_cur: u64,
3016    pub rlim_max: u64,
3017}
3018
3019pub const RLIMIT_NOFILE: c_int = 7;
3020pub const RLIMIT_STACK: c_int = 3;
3021pub const RLIMIT_DATA: c_int = 2;
3022pub const RLIMIT_AS: c_int = 9;
3023pub const RLIMIT_CPU: c_int = 0;
3024pub const RLIMIT_FSIZE: c_int = 1;
3025pub const RLIMIT_CORE: c_int = 4;
3026pub const RLIMIT_RSS: c_int = 5;
3027pub const RLIMIT_NPROC: c_int = 6;
3028pub const RLIMIT_MEMLOCK: c_int = 8;
3029pub const RLIMIT_LOCKS: c_int = 10;
3030pub const RLIMIT_SIGPENDING: c_int = 11;
3031pub const RLIMIT_MSGQUEUE: c_int = 12;
3032pub const RLIMIT_NICE: c_int = 13;
3033pub const RLIMIT_RTPRIO: c_int = 14;
3034pub const RLIMIT_RTTIME: c_int = 15;
3035pub const RLIM_INFINITY: u64 = !0u64;
3036
3037#[no_mangle]
3038pub unsafe extern "C" fn getrlimit(resource: c_int, rlim: *mut Rlimit) -> c_int {
3039    let r = sys_getrlimit(resource, rlim as *mut u8);
3040    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
3041}
3042
3043#[no_mangle]
3044pub unsafe extern "C" fn setrlimit(resource: c_int, rlim: *const Rlimit) -> c_int {
3045    let r = sys_setrlimit(resource, rlim as *const u8);
3046    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
3047}
3048
3049// ============================================================
3050// utimensat / futimens
3051// ============================================================
3052
3053#[no_mangle]
3054pub unsafe extern "C" fn utimensat(dirfd: c_int, path: *const c_char, times: *const timespec, flags: c_int) -> c_int {
3055    let r = sys_utimensat(dirfd, path, times as *const u8, flags);
3056    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
3057}
3058
3059#[no_mangle]
3060pub unsafe extern "C" fn futimens(fd: c_int, times: *const timespec) -> c_int {
3061    let r = sys_utimensat(fd, core::ptr::null(), times as *const u8, 0);
3062    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
3063}
3064
3065// ============================================================
3066// sys/socket.h
3067// ============================================================
3068
3069pub const AF_UNIX: c_int = 1;
3070pub const AF_INET: c_int = 2;
3071pub const AF_INET6: c_int = 10;
3072
3073pub const SOCK_STREAM: c_int = 1;
3074pub const SOCK_DGRAM: c_int = 2;
3075
3076pub const SHUT_RD: c_int = 0;
3077pub const SHUT_WR: c_int = 1;
3078pub const SHUT_RDWR: c_int = 2;
3079
3080pub const SOL_SOCKET: c_int = 1;
3081pub const SO_REUSEADDR: c_int = 2;
3082
3083#[repr(C)]
3084pub struct sockaddr {
3085    pub sa_family: u16,
3086    pub sa_data: [u8; 14],
3087}
3088
3089#[inline]
3090unsafe fn sys_socket(domain: c_int, ty: c_int, protocol: c_int) -> i64 {
3091    <Arch as Syscalls>::syscall3(SYS_SOCKET, domain as i64, ty as i64, protocol as i64)
3092}
3093
3094#[inline]
3095unsafe fn sys_socketpair(domain: c_int, ty: c_int, protocol: c_int, sv: *mut c_int) -> i64 {
3096    <Arch as Syscalls>::syscall4(SYS_SOCKETPAIR, domain as i64, ty as i64, protocol as i64, sv as i64)
3097}
3098
3099#[inline]
3100unsafe fn sys_bind(fd: c_int, addr: *const sockaddr, len: c_uint) -> i64 {
3101    <Arch as Syscalls>::syscall3(SYS_BIND, fd as i64, addr as i64, len as i64)
3102}
3103
3104#[inline]
3105unsafe fn sys_listen(fd: c_int, backlog: c_int) -> i64 {
3106    <Arch as Syscalls>::syscall2(SYS_LISTEN, fd as i64, backlog as i64)
3107}
3108
3109#[inline]
3110unsafe fn sys_accept(fd: c_int, addr: *mut sockaddr, len: *mut c_uint) -> i64 {
3111    <Arch as Syscalls>::syscall3(SYS_ACCEPT, fd as i64, addr as i64, len as i64)
3112}
3113
3114#[inline]
3115unsafe fn sys_connect(fd: c_int, addr: *const sockaddr, len: c_uint) -> i64 {
3116    <Arch as Syscalls>::syscall3(SYS_CONNECT, fd as i64, addr as i64, len as i64)
3117}
3118
3119#[inline]
3120unsafe fn sys_sendto(fd: c_int,
3121    buf: *const c_void,
3122    len: usize,
3123    flags: c_int,
3124    addr: *const sockaddr,
3125    addrlen: c_uint,) -> i64 {
3126    <Arch as Syscalls>::syscall6(SYS_SENDTO, fd as i64, buf as i64, len as i64, flags as i64, addr as i64, addrlen as i64)
3127}
3128
3129#[inline]
3130unsafe fn sys_recvfrom(fd: c_int,
3131    buf: *mut c_void,
3132    len: usize,
3133    flags: c_int,
3134    addr: *mut sockaddr,
3135    addrlen: *mut c_uint,) -> i64 {
3136    <Arch as Syscalls>::syscall6(SYS_RECVFROM, fd as i64, buf as i64, len as i64, flags as i64, addr as i64, addrlen as i64)
3137}
3138
3139#[inline]
3140unsafe fn sys_shutdown(fd: c_int, how: c_int) -> i64 {
3141    <Arch as Syscalls>::syscall2(SYS_SHUTDOWN, fd as i64, how as i64)
3142}
3143
3144#[inline]
3145unsafe fn sys_setsockopt(fd: c_int,
3146    level: c_int,
3147    optname: c_int,
3148    optval: *const c_void,
3149    optlen: c_uint,) -> i64 {
3150    <Arch as Syscalls>::syscall5(SYS_SETSOCKOPT, fd as i64, level as i64, optname as i64, optval as i64, optlen as i64)
3151}
3152
3153#[inline]
3154unsafe fn sys_getsockname(fd: c_int, addr: *mut sockaddr, len: *mut c_uint) -> i64 {
3155    <Arch as Syscalls>::syscall3(SYS_GETSOCKNAME, fd as i64, addr as i64, len as i64)
3156}
3157
3158#[no_mangle]
3159pub unsafe extern "C" fn socket(domain: c_int, ty: c_int, protocol: c_int) -> c_int {
3160    let r = sys_socket(domain, ty, protocol);
3161    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
3162    r as c_int
3163}
3164
3165#[no_mangle]
3166pub unsafe extern "C" fn socketpair(domain: c_int, ty: c_int, protocol: c_int, sv: *mut c_int) -> c_int {
3167    let r = sys_socketpair(domain, ty, protocol, sv);
3168    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
3169    0
3170}
3171
3172#[no_mangle]
3173pub unsafe extern "C" fn bind(fd: c_int, addr: *const sockaddr, len: c_uint) -> c_int {
3174    let r = sys_bind(fd, addr, len);
3175    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
3176    0
3177}
3178
3179#[no_mangle]
3180pub unsafe extern "C" fn listen(fd: c_int, backlog: c_int) -> c_int {
3181    let r = sys_listen(fd, backlog);
3182    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
3183    0
3184}
3185
3186#[no_mangle]
3187pub unsafe extern "C" fn accept(fd: c_int, addr: *mut sockaddr, len: *mut c_uint) -> c_int {
3188    let r = sys_accept(fd, addr, len);
3189    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
3190    r as c_int
3191}
3192
3193#[no_mangle]
3194pub unsafe extern "C" fn connect(fd: c_int, addr: *const sockaddr, len: c_uint) -> c_int {
3195    let r = sys_connect(fd, addr, len);
3196    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
3197    0
3198}
3199
3200#[no_mangle]
3201pub unsafe extern "C" fn send(fd: c_int, buf: *const c_void, len: usize, flags: c_int) -> isize {
3202    let r = sys_sendto(fd, buf, len, flags, core::ptr::null(), 0);
3203    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
3204    r as isize
3205}
3206
3207#[no_mangle]
3208pub unsafe extern "C" fn recv(fd: c_int, buf: *mut c_void, len: usize, flags: c_int) -> isize {
3209    let r = sys_recvfrom(fd, buf, len, flags, core::ptr::null_mut(), core::ptr::null_mut());
3210    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
3211    r as isize
3212}
3213
3214#[no_mangle]
3215pub unsafe extern "C" fn sendto(
3216    fd: c_int,
3217    buf: *const c_void,
3218    len: usize,
3219    flags: c_int,
3220    addr: *const sockaddr,
3221    addrlen: c_uint,
3222) -> isize {
3223    let r = sys_sendto(fd, buf, len, flags, addr, addrlen);
3224    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
3225    r as isize
3226}
3227
3228#[no_mangle]
3229pub unsafe extern "C" fn recvfrom(
3230    fd: c_int,
3231    buf: *mut c_void,
3232    len: usize,
3233    flags: c_int,
3234    addr: *mut sockaddr,
3235    addrlen: *mut c_uint,
3236) -> isize {
3237    let r = sys_recvfrom(fd, buf, len, flags, addr, addrlen);
3238    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
3239    r as isize
3240}
3241
3242#[no_mangle]
3243pub unsafe extern "C" fn shutdown(fd: c_int, how: c_int) -> c_int {
3244    let r = sys_shutdown(fd, how);
3245    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
3246    0
3247}
3248
3249#[no_mangle]
3250pub unsafe extern "C" fn setsockopt(
3251    fd: c_int,
3252    level: c_int,
3253    optname: c_int,
3254    optval: *const c_void,
3255    optlen: c_uint,
3256) -> c_int {
3257    let r = sys_setsockopt(fd, level, optname, optval, optlen);
3258    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
3259    0
3260}
3261
3262#[no_mangle]
3263pub unsafe extern "C" fn getsockname(fd: c_int, addr: *mut sockaddr, len: *mut c_uint) -> c_int {
3264    let r = sys_getsockname(fd, addr, len);
3265    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
3266    0
3267}
3268
3269#[no_mangle]
3270pub extern "C" fn htonl(hostlong: c_uint) -> c_uint {
3271    hostlong.to_be()
3272}
3273
3274#[no_mangle]
3275pub extern "C" fn ntohl(netlong: c_uint) -> c_uint {
3276    c_uint::from_be(netlong)
3277}
3278
3279#[no_mangle]
3280pub extern "C" fn htons(hostshort: u16) -> u16 {
3281    hostshort.to_be()
3282}
3283
3284#[no_mangle]
3285pub extern "C" fn ntohs(netshort: u16) -> u16 {
3286    u16::from_be(netshort)
3287}
3288
3289pub type PthreadT = c_ulong;
3290
3291// pthread_attr_t: musl x86_64 layout (56 bytes = int[14])
3292#[repr(C)]
3293pub struct pthread_attr_t {
3294    __i: [c_int; 14],
3295}
3296
3297// pthread_mutex_t: musl x86_64 layout (40 bytes = int[10])
3298// _m_type=__i[0], _m_lock=__i[1], _m_waiters=__i[2], _m_count=__i[5]
3299#[repr(C)]
3300pub struct pthread_mutex_t {
3301    __i: [c_int; 10],
3302}
3303
3304#[repr(C)]
3305pub struct pthread_mutexattr_t {
3306    __attr: c_uint,
3307}
3308
3309// pthread_cond_t: musl x86_64 layout (48 bytes = int[12])
3310// _c_seq=__i[2], _c_waiters=__i[3], _c_clock=__i[4]
3311#[repr(C)]
3312pub struct pthread_cond_t {
3313    __i: [c_int; 12],
3314}
3315
3316#[repr(C)]
3317pub struct pthread_condattr_t {
3318    __attr: c_uint,
3319}
3320
3321// pthread_rwlock_t: musl x86_64 layout (56 bytes = int[14])
3322// _rw_lock=__i[0], _rw_waiters=__i[1], _rw_shared=__i[2]
3323#[repr(C)]
3324pub struct pthread_rwlock_t {
3325    __i: [c_int; 14],
3326}
3327
3328#[repr(C)]
3329pub struct pthread_rwlockattr_t {
3330    __attr: [c_uint; 2],
3331}
3332
3333// pthread_barrier_t: musl x86_64 layout (32 bytes = int[8])
3334#[repr(C)]
3335pub struct pthread_barrier_t {
3336    __i: [c_int; 8],
3337}
3338
3339#[repr(C)]
3340pub struct pthread_barrierattr_t {
3341    __attr: c_uint,
3342}
3343
3344pub type pthread_spinlock_t = c_int;
3345pub type pthread_once_t = c_int;
3346pub type pthread_key_t = c_uint;
3347
3348// sem_t: musl x86_64 layout (32 bytes = int[8])
3349#[repr(C)]
3350pub struct sem_t {
3351    __val: [c_int; 8],
3352}
3353
3354#[repr(C)]
3355pub struct sched_param {
3356    pub sched_priority: c_int,
3357}
3358
3359const CLONE_VM: c_ulong = 0x00000100;
3360const CLONE_FS: c_ulong = 0x00000200;
3361const CLONE_FILES: c_ulong = 0x00000400;
3362const CLONE_SIGHAND: c_ulong = 0x00000800;
3363const CLONE_THREAD: c_ulong = 0x00010000;
3364const CLONE_SYSVSEM: c_ulong = 0x00040000;
3365const CLONE_SETTLS: c_ulong = 0x00080000;
3366const CLONE_PARENT_SETTID: c_ulong = 0x00100000;
3367const CLONE_CHILD_CLEARTID: c_ulong = 0x00200000;
3368
3369const FUTEX_WAIT: c_int = 0;
3370const FUTEX_WAKE: c_int = 1;
3371const FUTEX_LOCK_PI: c_int = 6;
3372const FUTEX_UNLOCK_PI: c_int = 7;
3373const FUTEX_TRYLOCK_PI: c_int = 8;
3374
3375// ponytail: SIGCANCEL (32) is musl's internal cancel signal
3376const SIGCANCEL: c_int = 32;
3377
3378const PTHREAD_MUTEX_NORMAL: c_int = 0;
3379const PTHREAD_MUTEX_DEFAULT: c_int = 0;
3380const PTHREAD_MUTEX_RECURSIVE: c_int = 1;
3381const PTHREAD_MUTEX_ERRORCHECK: c_int = 2;
3382
3383const PTHREAD_CREATE_JOINABLE: c_int = 0;
3384const PTHREAD_CREATE_DETACHED: c_int = 1;
3385
3386const PTHREAD_CANCEL_ENABLE: c_int = 0;
3387const PTHREAD_CANCEL_DISABLE: c_int = 1;
3388const PTHREAD_CANCEL_DEFERRED: c_int = 0;
3389const PTHREAD_CANCEL_ASYNCHRONOUS: c_int = 1;
3390
3391const PTHREAD_SCOPE_SYSTEM: c_int = 0;
3392const PTHREAD_PROCESS_PRIVATE: c_int = 0;
3393const PTHREAD_PROCESS_SHARED: c_int = 1;
3394const PTHREAD_INHERIT_SCHED: c_int = 0;
3395const PTHREAD_EXPLICIT_SCHED: c_int = 1;
3396
3397const PTHREAD_BARRIER_SERIAL_THREAD: c_int = -1;
3398
3399const PTHREAD_KEYS_MAX: usize = 128;
3400const PTHREAD_DESTRUCTOR_ITERATIONS: usize = 4;
3401const SEM_VALUE_MAX: c_int = 0x7fffffff;
3402
3403const DT_EXITED: c_int = 0;
3404const DT_EXITING: c_int = 1;
3405const DT_JOINABLE: c_int = 2;
3406const DT_DETACHED: c_int = 3;
3407
3408// ponytail: musl __ptcb cleanup handler node
3409#[repr(C)]
3410#[derive(Copy, Clone)]
3411pub struct __ptcb {
3412    __f: Option<unsafe extern "C" fn(*mut c_void)>,
3413    __x: *mut c_void,
3414    __next: *mut __ptcb,
3415}
3416
3417// ponytail: robust_list_head for kernel robust mutex support
3418#[repr(C)]
3419#[derive(Copy, Clone)]
3420struct robust_list {
3421    next: *mut robust_list,
3422}
3423
3424#[repr(C)]
3425#[derive(Copy, Clone)]
3426struct robust_list_head {
3427    list: robust_list,
3428    futex_offset: c_long,
3429    pending: *mut robust_list,
3430}
3431
3432// ponytail: adapted from musl x86_64 clone.s
3433#[cfg(all(target_arch = "x86_64", not(test)))]
3434core::arch::global_asm!(
3435    ".global __rc_clone",
3436    ".type __rc_clone, @function",
3437    "__rc_clone:",
3438    "mov rax, rdi",
3439    "mov rdi, rdx",
3440    "mov rdx, r8",
3441    "mov r10, [rsp + 8]",
3442    "mov r8, r9",
3443    "mov r9, rax",
3444    "and rsi, -16",
3445    "sub rsi, 8",
3446    "mov [rsi], rcx",
3447    "mov eax, 56",
3448    "syscall",
3449    "test rax, rax",
3450    "jnz 1f",
3451    "pop rdi",
3452    "call r9",
3453    // A clone() child that returns from its function must exit with that
3454    // value (musl does the same); falling through to hlt raises SIGSEGV.
3455    "mov edi, eax",
3456    "mov eax, 60",
3457    "syscall",
3458    "hlt",
3459    "1:",
3460    "ret",
3461);
3462
3463// ponytail: adapted from musl aarch64 clone.s
3464#[no_mangle]
3465#[unsafe(naked)]
3466#[cfg(target_arch = "aarch64")]
3467pub unsafe extern "C" fn __rc_clone(
3468    fn_: usize,
3469    stack: *mut u8,
3470    flags: c_ulong,
3471    arg: *mut c_void,
3472    ptid: *mut c_int,
3473    tls: c_ulong,
3474    ctid: *mut c_int,
3475) -> i64 {
3476    core::arch::naked_asm!(
3477        "and x1, x1, #-16",
3478        "stp x0, x3, [x1, #-16]!",
3479        "uxtw x0, w2",
3480        "mov x2, x4",
3481        "mov x3, x5",
3482        "mov x4, x6",
3483        "mov x8, #220",
3484        "svc #0",
3485        "cbz x0, 1f",
3486        "ret",
3487        "1:",
3488        "mov x29, #0",
3489        "ldp x1, x0, [sp], #16",
3490        "blr x1",
3491        "mov x8, #93",
3492        "svc #0",
3493    );
3494}
3495
3496#[no_mangle]
3497#[unsafe(naked)]
3498#[cfg(target_arch = "riscv64")]
3499pub unsafe extern "C" fn __rc_clone(
3500    fn_: usize,
3501    stack: *mut u8,
3502    flags: c_ulong,
3503    arg: *mut c_void,
3504    ptid: *mut c_int,
3505    tls: c_ulong,
3506    ctid: *mut c_int,
3507) -> i64 {
3508    core::arch::naked_asm!(
3509        "andi a1, a1, -16",
3510        "addi a1, a1, -16",
3511        "sd a0, 0(a1)",
3512        "sd a3, 8(a1)",
3513        "mv a0, a2",
3514        "mv a2, a4",
3515        "mv a3, a5",
3516        "mv a4, a6",
3517        "li a7, 220",
3518        "ecall",
3519        "beqz a0, 1f",
3520        "ret",
3521        "1:",
3522        "ld a1, 0(sp)",
3523        "ld a0, 8(sp)",
3524        "jalr a1",
3525        "li a7, 93",
3526        "ecall",
3527    );
3528}
3529
3530#[cfg(all(target_arch = "x86_64", test))]
3531#[inline(never)]
3532unsafe fn __rc_clone(
3533    _fn_: usize,
3534    _stack: *mut u8,
3535    _flags: c_ulong,
3536    _arg: *mut c_void,
3537    _ptid: *mut c_int,
3538    _tls: c_ulong,
3539    _ctid: *mut c_int,
3540) -> i64 {
3541    -1
3542}
3543
3544#[cfg(all(target_arch = "x86_64", not(test)))]
3545extern "C" {
3546    fn __rc_clone(
3547        fn_: usize,
3548        stack: *mut u8,
3549        flags: c_ulong,
3550        arg: *mut c_void,
3551        ptid: *mut c_int,
3552        tls: c_ulong,
3553        ctid: *mut c_int,
3554    ) -> i64;
3555}
3556
3557#[inline(never)]
3558#[linkage = "weak"]
3559#[no_mangle]
3560pub extern "C" fn __rc_create_thread_tls() -> *mut u8 {
3561    core::ptr::null_mut()
3562}
3563
3564#[inline(never)]
3565#[linkage = "weak"]
3566#[no_mangle]
3567pub unsafe extern "C" fn __rc_init_thread_tls_at(_block: *mut u8) -> *mut u8 {
3568    core::ptr::null_mut()
3569}
3570
3571#[inline(never)]
3572#[linkage = "weak"]
3573#[no_mangle]
3574pub extern "C" fn __rc_tls_block_size() -> usize {
3575    0
3576}
3577
3578#[inline(never)]
3579#[linkage = "weak"]
3580#[no_mangle]
3581pub extern "C" fn __rc_tls_base_offset() -> usize {
3582    0
3583}
3584
3585#[inline]
3586unsafe fn sys_gettid() -> i64 {
3587    <Arch as Syscalls>::syscall0(SYS_GETTID)
3588}
3589
3590#[inline]
3591unsafe fn sys_futex(uaddr: *mut c_int,
3592    futex_op: c_int,
3593    val: c_int,
3594    timeout: *mut c_void,
3595    uaddr2: *mut c_int,
3596    val3: c_int,) -> i64 {
3597    <Arch as Syscalls>::syscall6(SYS_FUTEX, uaddr as i64, futex_op as i64, val as i64, timeout as i64, uaddr2 as i64, val3 as i64)
3598}
3599
3600const MAX_THREADS: usize = 1024;
3601const STACK_SIZE: usize = 1024 * 1024;
3602
3603#[repr(C)]
3604#[derive(Copy, Clone)]
3605struct Thread {
3606    tid: c_int,
3607    detach_state: c_int,
3608    result: *mut c_void,
3609    cancel: c_int,
3610    cancel_state: c_int,
3611    cancel_type: c_int,
3612    user_fn: usize,
3613    user_arg: *mut c_void,
3614    stack: *mut u8,
3615    stack_size: usize,
3616    stack_owned: bool,
3617    fs_base: *mut u8,
3618    // TLS block as allocated at creation. The global TLS size can grow when
3619    // a DSO is dlopen'ed, so it must not be used to free an older thread's
3620    // block: the address and length would be wrong and could unmap a
3621    // neighbouring thread stack.
3622    tls_base: *mut u8,
3623    tls_size: usize,
3624    tsd: [*mut c_void; PTHREAD_KEYS_MAX],
3625    cancelbuf: *mut __ptcb,
3626    robust_list: robust_list_head,
3627}
3628
3629static mut THREADS: [Thread; MAX_THREADS] = [Thread {
3630    tid: -1,
3631    detach_state: DT_JOINABLE,
3632    result: core::ptr::null_mut(),
3633    cancel: 0,
3634    cancel_state: PTHREAD_CANCEL_ENABLE,
3635    cancel_type: PTHREAD_CANCEL_DEFERRED,
3636    user_fn: 0,
3637    user_arg: core::ptr::null_mut(),
3638    stack: core::ptr::null_mut(),
3639    stack_size: 0,
3640    stack_owned: false,
3641    fs_base: core::ptr::null_mut(),
3642    tls_base: core::ptr::null_mut(),
3643    tls_size: 0,
3644    tsd: [core::ptr::null_mut(); PTHREAD_KEYS_MAX],
3645    cancelbuf: core::ptr::null_mut(),
3646    robust_list: robust_list_head {
3647        list: robust_list { next: core::ptr::null_mut() },
3648        futex_offset: 0,
3649        pending: core::ptr::null_mut(),
3650    },
3651}; MAX_THREADS];
3652const SLOT_WORDS: usize = (MAX_THREADS + usize::BITS as usize - 1) / usize::BITS as usize;
3653static SLOT_USED: [AtomicUsize; SLOT_WORDS] = [const { AtomicUsize::new(0) }; SLOT_WORDS];
3654static mut KEY_DTORS: [Option<unsafe extern "C" fn(*mut c_void)>; PTHREAD_KEYS_MAX] = [None; PTHREAD_KEYS_MAX];
3655static NEXT_KEY: AtomicUsize = AtomicUsize::new(0);
3656
3657// ponytail: futex helpers
3658unsafe fn futex_wait(addr: *mut c_int, expected: c_int) -> c_int {
3659    let r = sys_futex(addr, FUTEX_WAIT, expected, null_mut(), null_mut(), 0);
3660    if r < 0 { let e = (-r) as c_int; if e == EAGAIN { 0 } else { e } } else { 0 }
3661}
3662
3663unsafe fn futex_wake(addr: *mut c_int, count: c_int) {
3664    let c = if count < 0 { c_int::MAX } else { count };
3665    sys_futex(addr, FUTEX_WAKE, c, null_mut(), null_mut(), 0);
3666}
3667
3668unsafe fn futex_lock_pi(addr: *mut c_int, abs_timeout: *const timespec) -> c_int {
3669    let timeout_ptr = if abs_timeout.is_null() { null_mut() } else { abs_timeout as *mut c_void };
3670    let r = sys_futex(addr, FUTEX_LOCK_PI, 0, timeout_ptr, null_mut(), 0);
3671    if r < 0 { (-r) as c_int } else { 0 }
3672}
3673
3674unsafe fn futex_unlock_pi(addr: *mut c_int) -> c_int {
3675    let r = sys_futex(addr, FUTEX_UNLOCK_PI, 0, null_mut(), null_mut(), 0);
3676    if r < 0 { (-r) as c_int } else { 0 }
3677}
3678
3679#[inline]
3680unsafe fn sys_set_robust_list(head: *mut robust_list_head, len: usize) -> i64 {
3681    <Arch as Syscalls>::syscall2(SYS_SET_ROBUST_LIST, head as i64, len as i64)
3682}
3683
3684unsafe fn futex_timedwait(addr: *mut c_int, expected: c_int, abs_timeout: *const timespec) -> c_int {
3685    if abs_timeout.is_null() { return futex_wait(addr, expected); }
3686    let mut now: timespec = core::mem::zeroed();
3687    if sys_clock_gettime(CLOCK_REALTIME, &mut now) < 0 { return EINVAL; }
3688    let mut rel: timespec = core::mem::zeroed();
3689    rel.tv_sec = (*abs_timeout).tv_sec - now.tv_sec;
3690    rel.tv_nsec = (*abs_timeout).tv_nsec - now.tv_nsec;
3691    if rel.tv_nsec < 0 { rel.tv_sec -= 1; rel.tv_nsec += 1_000_000_000; }
3692    if rel.tv_sec < 0 { return ETIMEDOUT; }
3693    let r = sys_futex(addr, FUTEX_WAIT, expected, &mut rel as *mut timespec as *mut c_void, null_mut(), 0);
3694    if r < 0 { let e = (-r) as c_int; if e == EAGAIN { 0 } else { e } } else { 0 }
3695}
3696
3697// ponytail: atomic helpers wrapping AtomicI32
3698unsafe fn a_cas(addr: *mut c_int, expected: c_int, desired: c_int) -> c_int {
3699    let a = &*(addr as *const AtomicI32);
3700    match a.compare_exchange(expected, desired, Ordering::AcqRel, Ordering::Acquire) {
3701        Ok(v) | Err(v) => v,
3702    }
3703}
3704
3705unsafe fn a_store(addr: *mut c_int, val: c_int) {
3706    (*(addr as *const AtomicI32)).store(val, Ordering::Release);
3707}
3708
3709unsafe fn a_load(addr: *const c_int) -> c_int {
3710    (*(addr as *const AtomicI32)).load(Ordering::Acquire)
3711}
3712
3713unsafe fn a_swap(addr: *mut c_int, val: c_int) -> c_int {
3714    (*(addr as *const AtomicI32)).swap(val, Ordering::AcqRel)
3715}
3716
3717unsafe fn a_fetch_add(addr: *mut c_int, val: c_int) -> c_int {
3718    (*(addr as *const AtomicI32)).fetch_add(val, Ordering::AcqRel)
3719}
3720
3721unsafe fn a_fetch_sub(addr: *mut c_int, val: c_int) -> c_int {
3722    (*(addr as *const AtomicI32)).fetch_sub(val, Ordering::AcqRel)
3723}
3724
3725unsafe fn spinlock_lock(lock: *mut c_int, waiters: *mut c_int) {
3726    while a_swap(lock, 1) != 0 {
3727        let mut spins = 100;
3728        while spins > 0 && a_load(lock) != 0 { core::hint::spin_loop(); spins -= 1; }
3729        if a_load(lock) == 0 { continue; }
3730        a_fetch_add(waiters, 1);
3731        futex_wait(lock, 1);
3732        a_fetch_sub(waiters, 1);
3733    }
3734}
3735
3736unsafe fn spinlock_unlock(lock: *mut c_int, waiters: *mut c_int) {
3737    a_store(lock, 0);
3738    if a_load(waiters) > 0 { futex_wake(lock, 1); }
3739}
3740
3741unsafe fn slot_index(slot: *const Thread) -> usize {
3742    let base = core::ptr::addr_of!(THREADS[0]) as usize;
3743    (slot as usize - base) / core::mem::size_of::<Thread>()
3744}
3745
3746unsafe fn release_thread_slot(slot: *const Thread) {
3747    let idx = slot_index(slot);
3748    if idx < MAX_THREADS {
3749        let word = idx / usize::BITS as usize;
3750        let bit = idx % usize::BITS as usize;
3751        SLOT_USED[word].fetch_and(!(1usize << bit), Ordering::AcqRel);
3752    }
3753}
3754
3755unsafe fn alloc_thread_slot() -> Option<&'static mut Thread> {
3756    for word in 0..SLOT_WORDS {
3757        let mut used = SLOT_USED[word].load(Ordering::Acquire);
3758        loop {
3759            let free = !used;
3760            if free == 0 {
3761                break;
3762            }
3763            let bit = free.trailing_zeros() as usize;
3764            let idx = word * usize::BITS as usize + bit;
3765            if idx >= MAX_THREADS {
3766                break;
3767            }
3768            match SLOT_USED[word].compare_exchange_weak(
3769                used,
3770                used | (1usize << bit),
3771                Ordering::AcqRel,
3772                Ordering::Acquire,
3773            ) {
3774                Ok(_) => return Some(&mut THREADS[idx]),
3775                Err(v) => used = v,
3776            }
3777        }
3778    }
3779    None
3780}
3781
3782unsafe fn run_key_dtors(slot: &mut Thread) {
3783    for _ in 0..PTHREAD_DESTRUCTOR_ITERATIONS {
3784        let mut any = false;
3785        for i in 0..PTHREAD_KEYS_MAX {
3786            let val = slot.tsd[i];
3787            if !val.is_null() {
3788                if let Some(dtor) = KEY_DTORS[i] {
3789                    slot.tsd[i] = core::ptr::null_mut();
3790                    any = true;
3791                    dtor(val);
3792                }
3793            }
3794        }
3795        if !any { break; }
3796    }
3797}
3798
3799unsafe extern "C" fn thread_entry(slot: *mut c_void) -> *mut c_void {
3800    let slot = &mut *(slot as *mut Thread);
3801    // A reused slot may still carry the previous thread's robust-list state;
3802    // register a fresh list for this thread.
3803    slot.robust_list.list.next = core::ptr::null_mut();
3804    slot.robust_list.futex_offset = 0;
3805    slot.robust_list.pending = core::ptr::null_mut();
3806    ensure_robust_list(slot);
3807    let mut set: SigSetT = 0;
3808    sigemptyset(&mut set);
3809    sigaddset(&mut set, SIGCANCEL);
3810    pthread_sigmask(SIG_UNBLOCK, &set, core::ptr::null_mut());
3811    let user_fn: unsafe extern "C" fn(*mut c_void) -> *mut c_void =
3812        core::mem::transmute::<usize, _>(slot.user_fn);
3813    let ret = user_fn(slot.user_arg);
3814    run_key_dtors(slot);
3815    slot.result = ret;
3816    a_store(&raw mut slot.detach_state, DT_EXITED);
3817    futex_wake(&raw mut slot.detach_state, 1);
3818    sys_exit_thread(0)
3819}
3820
3821unsafe fn find_thread() -> Option<&'static mut Thread> {
3822    let me = sys_gettid() as c_int;
3823    let base = core::ptr::addr_of_mut!(THREADS[0]);
3824    for i in 0..MAX_THREADS {
3825        let slot = base.add(i);
3826        if core::ptr::read_volatile(&raw const (*slot).tid) == me {
3827            return Some(&mut *slot);
3828        }
3829    }
3830    // Auto-register the current thread (usually the main thread) on first use.
3831    pthread_self();
3832    for i in 0..MAX_THREADS {
3833        let slot = base.add(i);
3834        if core::ptr::read_volatile(&raw const (*slot).tid) == me {
3835            return Some(&mut *slot);
3836        }
3837    }
3838    None
3839}
3840
3841#[no_mangle]
3842pub unsafe extern "C" fn pthread_self() -> PthreadT {
3843    let me = sys_gettid() as c_int;
3844    let base = core::ptr::addr_of_mut!(THREADS[0]);
3845    for i in 0..MAX_THREADS {
3846        let slot = base.add(i);
3847        if core::ptr::read_volatile(&raw const (*slot).tid) == me {
3848            return slot as PthreadT;
3849        }
3850    }
3851    // Register the main thread lazily in a free slot.
3852    if let Some(slot) = alloc_thread_slot() {
3853        let slot = slot as *mut Thread;
3854        (*slot).tid = me;
3855        (*slot).detach_state = DT_JOINABLE;
3856        return slot as PthreadT;
3857    }
3858    0
3859}
3860
3861#[no_mangle]
3862pub unsafe extern "C" fn pthread_equal(t1: PthreadT, t2: PthreadT) -> c_int {
3863    (t1 == t2) as c_int
3864}
3865
3866#[no_mangle]
3867pub unsafe extern "C" fn pthread_create(
3868    thread: *mut PthreadT,
3869    attr: *const pthread_attr_t,
3870    start_routine: usize,
3871    arg: *mut c_void,
3872) -> c_int {
3873    if start_routine == 0 || thread.is_null() { return EINVAL; }
3874    let Some(slot_ref) = alloc_thread_slot() else { return EAGAIN; };
3875    let slot = slot_ref as *mut Thread;
3876    let stack_size = if !attr.is_null() {
3877        let s = *((*attr).__i.as_ptr() as *const usize);
3878        if s > 0 { s } else { STACK_SIZE }
3879    } else { STACK_SIZE };
3880    // musl stores the stack *top* in pthread_attr_t; zero means "allocate".
3881    let user_top = if !attr.is_null() {
3882        *((*attr).__i.as_ptr().add(4) as *const *mut c_void)
3883    } else {
3884        core::ptr::null_mut()
3885    };
3886    let stack_owned = user_top.is_null();
3887    let stack = if stack_owned {
3888        sys_mmap(null_mut(), stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)
3889    } else {
3890        (user_top as *mut u8).sub(stack_size)
3891    };
3892    if stack == MMAP_FAILED { release_thread_slot(slot); return EAGAIN; }
3893    let static_mode = RC_STATIC_EXEC;
3894    let tls_total = if static_mode {
3895        STATIC_TLS_BLOCK_SIZE
3896    } else {
3897        __rc_tls_block_size()
3898    };
3899    let mut stack_top = stack.add(stack_size);
3900    let mut tls_owned = true;
3901    let fs_base = if static_mode {
3902        if !stack_owned && tls_total > 0 && stack_size > tls_total + 4096 {
3903            let block = ((user_top as usize - tls_total) & !63usize) as *mut u8;
3904            let fs = static_tls_init_block(block);
3905            if fs.is_null() {
3906                static_tls_create_block()
3907            } else {
3908                stack_top = block;
3909                tls_owned = false;
3910                fs
3911            }
3912        } else {
3913            static_tls_create_block()
3914        }
3915    } else if !stack_owned && tls_total > 0 && stack_size > tls_total + 4096 {
3916        // Carve TLS out of the top of the application-provided stack (musl
3917        // behaviour). A separate mmap here can land directly below the stack
3918        // and prevent MAP_GROWSDOWN from expanding it.
3919        let block = ((user_top as usize - tls_total) & !63usize) as *mut u8;
3920        let fs = __rc_init_thread_tls_at(block);
3921        if fs.is_null() {
3922            __rc_create_thread_tls()
3923        } else {
3924            stack_top = block;
3925            tls_owned = false;
3926            fs
3927        }
3928    } else {
3929        __rc_create_thread_tls()
3930    };
3931    if fs_base.is_null() {
3932        if stack_owned { sys_munmap(stack, stack_size); }
3933        release_thread_slot(slot);
3934        return EAGAIN;
3935    }
3936    let tls_base = if static_mode {
3937        fs_base.sub(STATIC_TLS_TP_OFFSET)
3938    } else {
3939        fs_base.sub(__rc_tls_base_offset())
3940    };
3941    let tls_size = if tls_owned { tls_total } else { 0 };
3942    (*slot).detach_state = DT_JOINABLE;
3943    (*slot).result = core::ptr::null_mut();
3944    (*slot).cancel = 0;
3945    (*slot).cancel_state = PTHREAD_CANCEL_ENABLE;
3946    (*slot).cancel_type = PTHREAD_CANCEL_DEFERRED;
3947    core::ptr::write_bytes((*slot).tsd.as_mut_ptr(), 0, PTHREAD_KEYS_MAX);
3948    (*slot).tid = 1;
3949    (*slot).user_fn = start_routine;
3950    (*slot).user_arg = arg;
3951    (*slot).stack = stack;
3952    (*slot).stack_size = stack_size;
3953    (*slot).stack_owned = stack_owned;
3954    (*slot).fs_base = fs_base;
3955    (*slot).tls_base = tls_base;
3956    (*slot).tls_size = tls_size;
3957    let tid_ptr = &raw mut (*slot).tid;
3958    let flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD
3959        | CLONE_SYSVSEM | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | CLONE_SETTLS;
3960    let tid = __rc_clone(thread_entry as *const () as usize, stack_top, flags, slot as *mut c_void, tid_ptr, fs_base as c_ulong, tid_ptr);
3961    if tid < 0 {
3962        (*slot).tid = -1;
3963        if stack_owned { sys_munmap(stack, stack_size); }
3964        if tls_size > 0 { sys_munmap(tls_base, tls_size); }
3965        release_thread_slot(slot);
3966        return EAGAIN;
3967    }
3968    if !attr.is_null() && (*attr).__i[6] == PTHREAD_CREATE_DETACHED {
3969        (*slot).detach_state = DT_DETACHED;
3970    }
3971    *thread = slot as PthreadT;
3972    0
3973}
3974
3975#[no_mangle]
3976pub unsafe extern "C" fn pthread_join(thread: PthreadT, retval: *mut *mut c_void) -> c_int {
3977    let slot = thread as *mut Thread;
3978    if slot.is_null() { return EINVAL; }
3979    if (*slot).detach_state == DT_DETACHED { return EINVAL; }
3980    let tid_ptr = &raw mut (*slot).tid;
3981    loop {
3982        pthread_testcancel();
3983        let tid = core::ptr::read_volatile(tid_ptr);
3984        if tid == 0 { break; }
3985        if tid < 0 { return EINVAL; }
3986        sys_futex(tid_ptr, FUTEX_WAIT, tid, null_mut(), null_mut(), 0);
3987        pthread_testcancel();
3988    }
3989    if !retval.is_null() { *retval = (*slot).result; }
3990    let stack = (*slot).stack;
3991    let stack_size = (*slot).stack_size;
3992    let stack_owned = (*slot).stack_owned;
3993    let tls_base = (*slot).tls_base;
3994    let tls_size = (*slot).tls_size;
3995    (*slot).tid = -1;
3996    (*slot).detach_state = DT_JOINABLE;
3997    (*slot).result = core::ptr::null_mut();
3998    (*slot).stack = core::ptr::null_mut();
3999    (*slot).stack_size = 0;
4000    (*slot).stack_owned = false;
4001    (*slot).fs_base = core::ptr::null_mut();
4002    (*slot).tls_base = core::ptr::null_mut();
4003    (*slot).tls_size = 0;
4004    if stack_owned && !stack.is_null() && stack_size > 0 { sys_munmap(stack, stack_size); }
4005    // Keep the TLS mapping but return its pages to the kernel. Some runtimes
4006    // (notably V8) retain pointers into an exited thread's TLS and read them
4007    // during process teardown; unmapping makes that a SIGSEGV, while zero
4008    // pages are handled gracefully. The VA range is tiny (TLS_TOTAL_SIZE).
4009    // Retain the TLS block instead of unmapping it. V8 keeps pointers into a
4010    // joined thread's TLS and dereferences them during process teardown;
4011    // unmapping turns that into SIGSEGV, and MADV_DONTNEED races with it.
4012    // The block is small (TLS_TOTAL_SIZE + TCB_SIZE) and bounded by thread
4013    // creation, so keeping it mapped is the safe trade-off.
4014    let _ = (tls_base, tls_size);
4015    release_thread_slot(slot);
4016    0
4017}
4018
4019#[no_mangle]
4020pub unsafe extern "C" fn pthread_detach(thread: PthreadT) -> c_int {
4021    let slot = thread as *mut Thread;
4022    if slot.is_null() { return EINVAL; }
4023    let old = a_cas(&raw mut (*slot).detach_state, DT_JOINABLE, DT_DETACHED);
4024    if old == DT_EXITED {
4025        let stack = (*slot).stack;
4026        let stack_size = (*slot).stack_size;
4027        let stack_owned = (*slot).stack_owned;
4028        let tls_base = (*slot).tls_base;
4029        let tls_size = (*slot).tls_size;
4030        (*slot).tid = -1;
4031        (*slot).stack = core::ptr::null_mut();
4032        (*slot).stack_size = 0;
4033        (*slot).stack_owned = false;
4034        (*slot).fs_base = core::ptr::null_mut();
4035        (*slot).tls_base = core::ptr::null_mut();
4036        (*slot).tls_size = 0;
4037        if stack_owned && !stack.is_null() && stack_size > 0 { sys_munmap(stack, stack_size); }
4038        // See pthread_join: keep the TLS block mapped after the thread dies.
4039        let _ = (tls_base, tls_size);
4040        release_thread_slot(slot);
4041    }
4042    0
4043}
4044
4045#[no_mangle]
4046pub unsafe extern "C" fn pthread_exit(retval: *mut c_void) -> ! {
4047    if let Some(slot) = find_thread() {
4048        run_cleanup_handlers(slot);
4049        run_key_dtors(slot);
4050        slot.result = retval;
4051        slot.detach_state = DT_EXITED;
4052        futex_wake(&slot.detach_state as *const c_int as *mut c_int, 1);
4053
4054        // If this is the last thread, run atexit handlers via exit().
4055        let mut active = 0usize;
4056        for i in 0..MAX_THREADS {
4057            if THREADS[i].tid > 0 {
4058                active += 1;
4059            }
4060        }
4061        if active == 1 {
4062            exit(0);
4063        }
4064    }
4065    sys_exit_thread(0);
4066}
4067
4068// --- pthread_attr_* ---
4069#[no_mangle]
4070pub unsafe extern "C" fn pthread_attr_init(attr: *mut pthread_attr_t) -> c_int {
4071    core::ptr::write_bytes(attr, 0, 1);
4072    *((*attr).__i.as_mut_ptr() as *mut usize) = STACK_SIZE;
4073    *((*attr).__i.as_mut_ptr().add(2) as *mut usize) = 4096;
4074    (*attr).__i[6] = PTHREAD_CREATE_JOINABLE;
4075    (*attr).__i[7] = PTHREAD_INHERIT_SCHED;
4076    0
4077}
4078#[no_mangle]
4079pub unsafe extern "C" fn pthread_attr_destroy(_attr: *mut pthread_attr_t) -> c_int { 0 }
4080#[no_mangle]
4081pub unsafe extern "C" fn pthread_attr_setdetachstate(attr: *mut pthread_attr_t, s: c_int) -> c_int {
4082    if s != PTHREAD_CREATE_JOINABLE && s != PTHREAD_CREATE_DETACHED { return EINVAL; }
4083    (*attr).__i[6] = s; 0
4084}
4085#[no_mangle]
4086pub unsafe extern "C" fn pthread_attr_getdetachstate(attr: *const pthread_attr_t, s: *mut c_int) -> c_int {
4087    *s = (*attr).__i[6]; 0
4088}
4089#[no_mangle]
4090pub unsafe extern "C" fn pthread_attr_setstacksize(attr: *mut pthread_attr_t, sz: usize) -> c_int {
4091    if sz < 2048 { return EINVAL; }
4092    *((*attr).__i.as_mut_ptr() as *mut usize) = sz;
4093    *((*attr).__i.as_mut_ptr().add(4) as *mut *mut c_void) = core::ptr::null_mut();
4094    0
4095}
4096#[no_mangle]
4097pub unsafe extern "C" fn pthread_attr_getstacksize(attr: *const pthread_attr_t, sz: *mut usize) -> c_int {
4098    *sz = *((*attr).__i.as_ptr() as *const usize); 0
4099}
4100#[no_mangle]
4101pub unsafe extern "C" fn pthread_attr_setstack(attr: *mut pthread_attr_t, addr: *mut c_void, sz: usize) -> c_int {
4102    if sz < 2048 { return EINVAL; }
4103    *((*attr).__i.as_mut_ptr() as *mut usize) = sz;
4104    // musl stores the top of the stack, not its base.
4105    *((*attr).__i.as_mut_ptr().add(4) as *mut *mut c_void) = (addr as *mut u8).add(sz) as *mut c_void;
4106    0
4107}
4108#[no_mangle]
4109pub unsafe extern "C" fn pthread_attr_getstack(attr: *const pthread_attr_t, addr: *mut *mut c_void, sz: *mut usize) -> c_int {
4110    let size = *((*attr).__i.as_ptr() as *const usize);
4111    *sz = size;
4112    let top = *((*attr).__i.as_ptr().add(4) as *const *mut c_void);
4113    *addr = if top.is_null() {
4114        core::ptr::null_mut()
4115    } else {
4116        (top as *mut u8).sub(size) as *mut c_void
4117    };
4118    0
4119}
4120#[no_mangle]
4121pub unsafe extern "C" fn pthread_attr_setguardsize(attr: *mut pthread_attr_t, sz: usize) -> c_int {
4122    *((*attr).__i.as_mut_ptr().add(2) as *mut usize) = sz; 0
4123}
4124#[no_mangle]
4125pub unsafe extern "C" fn pthread_attr_getguardsize(attr: *const pthread_attr_t, sz: *mut usize) -> c_int {
4126    *sz = *((*attr).__i.as_ptr().add(2) as *const usize); 0
4127}
4128#[no_mangle]
4129pub unsafe extern "C" fn pthread_attr_setscope(_attr: *mut pthread_attr_t, scope: c_int) -> c_int {
4130    if scope != PTHREAD_SCOPE_SYSTEM { return EINVAL; }
4131    0
4132}
4133#[no_mangle]
4134pub unsafe extern "C" fn pthread_attr_getscope(_attr: *const pthread_attr_t, scope: *mut c_int) -> c_int {
4135    *scope = PTHREAD_SCOPE_SYSTEM; 0
4136}
4137#[no_mangle]
4138pub unsafe extern "C" fn pthread_attr_setinheritsched(attr: *mut pthread_attr_t, inh: c_int) -> c_int {
4139    if inh != PTHREAD_INHERIT_SCHED && inh != PTHREAD_EXPLICIT_SCHED { return EINVAL; }
4140    (*attr).__i[7] = inh; 0
4141}
4142#[no_mangle]
4143pub unsafe extern "C" fn pthread_attr_getinheritsched(attr: *const pthread_attr_t, inh: *mut c_int) -> c_int {
4144    *inh = (*attr).__i[7]; 0
4145}
4146#[no_mangle]
4147pub unsafe extern "C" fn pthread_attr_setschedpolicy(attr: *mut pthread_attr_t, p: c_int) -> c_int {
4148    (*attr).__i[8] = p; 0
4149}
4150#[no_mangle]
4151pub unsafe extern "C" fn pthread_attr_getschedpolicy(attr: *const pthread_attr_t, p: *mut c_int) -> c_int {
4152    *p = (*attr).__i[8]; 0
4153}
4154#[no_mangle]
4155pub unsafe extern "C" fn pthread_attr_setschedparam(attr: *mut pthread_attr_t, param: *const sched_param) -> c_int {
4156    (*attr).__i[9] = (*param).sched_priority; 0
4157}
4158#[no_mangle]
4159pub unsafe extern "C" fn pthread_attr_getschedparam(attr: *const pthread_attr_t, param: *mut sched_param) -> c_int {
4160    (*param).sched_priority = (*attr).__i[9]; 0
4161}
4162
4163// --- pthread_mutexattr_* ---
4164#[no_mangle]
4165pub unsafe extern "C" fn pthread_mutexattr_init(attr: *mut pthread_mutexattr_t) -> c_int {
4166    (*attr).__attr = 0; 0
4167}
4168#[no_mangle]
4169pub unsafe extern "C" fn pthread_mutexattr_destroy(_attr: *mut pthread_mutexattr_t) -> c_int { 0 }
4170#[no_mangle]
4171pub unsafe extern "C" fn pthread_mutexattr_settype(attr: *mut pthread_mutexattr_t, t: c_int) -> c_int {
4172    if (t as c_uint) > 2 { return EINVAL; }
4173    (*attr).__attr = ((*attr).__attr & !3) | t as c_uint; 0
4174}
4175#[no_mangle]
4176pub unsafe extern "C" fn pthread_mutexattr_gettype(attr: *const pthread_mutexattr_t, t: *mut c_int) -> c_int {
4177    *t = ((*attr).__attr & 3) as c_int; 0
4178}
4179#[no_mangle]
4180pub unsafe extern "C" fn pthread_mutexattr_setpshared(attr: *mut pthread_mutexattr_t, p: c_int) -> c_int {
4181    if (p as c_uint) > 1 { return EINVAL; }
4182    (*attr).__attr = ((*attr).__attr & !128) | ((p as c_uint) << 7); 0
4183}
4184#[no_mangle]
4185pub unsafe extern "C" fn pthread_mutexattr_getpshared(attr: *const pthread_mutexattr_t, p: *mut c_int) -> c_int {
4186    *p = (((*attr).__attr >> 7) & 1) as c_int; 0
4187}
4188
4189#[no_mangle]
4190pub unsafe extern "C" fn pthread_mutexattr_setprotocol(attr: *mut pthread_mutexattr_t, protocol: c_int) -> c_int {
4191    match protocol {
4192        0 => { (*attr).__attr &= !(MUTEX_PI as c_uint); 0 }
4193        1 => {
4194            let mut lk: c_int = 0;
4195            let r = sys_futex(&raw mut lk, FUTEX_LOCK_PI, 0, null_mut(), null_mut(), 0);
4196            if r < 0 { return (-r) as c_int; }
4197            sys_futex(&raw mut lk, FUTEX_UNLOCK_PI, 0, null_mut(), null_mut(), 0);
4198            (*attr).__attr |= MUTEX_PI as c_uint;
4199            0
4200        }
4201        2 => ENOTSUP,
4202        _ => EINVAL,
4203    }
4204}
4205#[no_mangle]
4206pub unsafe extern "C" fn pthread_mutexattr_getprotocol(attr: *const pthread_mutexattr_t, protocol: *mut c_int) -> c_int {
4207    *protocol = (((*attr).__attr >> 3) & 3) as c_int; 0
4208}
4209
4210const ENOTSUP: c_int = 95;
4211
4212#[no_mangle]
4213pub unsafe extern "C" fn pthread_mutexattr_setrobust(attr: *mut pthread_mutexattr_t, robust: c_int) -> c_int {
4214    if (robust as c_uint) > 1 { return EINVAL; }
4215    if robust != 0 {
4216        (*attr).__attr |= MUTEX_ROBUST as c_uint;
4217    } else {
4218        (*attr).__attr &= !(MUTEX_ROBUST as c_uint);
4219    }
4220    0
4221}
4222#[no_mangle]
4223pub unsafe extern "C" fn pthread_mutexattr_getrobust(attr: *const pthread_mutexattr_t, robust: *mut c_int) -> c_int {
4224    *robust = (((*attr).__attr >> 2) & 1) as c_int; 0
4225}
4226
4227#[no_mangle]
4228pub unsafe extern "C" fn pthread_mutex_consistent(m: *mut pthread_mutex_t) -> c_int {
4229    let old = a_load(&raw const (*m).__i[1]);
4230    let own = old & 0x3fffffff;
4231    if ((*m).__i[0] & MUTEX_ROBUST) == 0 || own == 0 || (old & 0x40000000) == 0 {
4232        return EINVAL;
4233    }
4234    let self_tid = sys_gettid() as c_int;
4235    if own != self_tid { return EPERM; }
4236    a_and(&raw mut (*m).__i[1], !(0x40000000i32));
4237    0
4238}
4239
4240unsafe fn a_and(addr: *mut c_int, val: c_int) {
4241    let a = &*(addr as *const AtomicI32);
4242    a.fetch_and(val, Ordering::AcqRel);
4243}
4244
4245// --- pthread_mutex_* ---
4246const MUTEX_TYPE_MASK: c_int = 3;
4247const MUTEX_ROBUST: c_int = 4;
4248const MUTEX_PI: c_int = 8;
4249const MUTEX_PSHARED: c_int = 128;
4250
4251unsafe fn ensure_robust_list(slot: &mut Thread) {
4252    if slot.robust_list.futex_offset == 0 {
4253        slot.robust_list.futex_offset = -28;
4254        slot.robust_list.pending = core::ptr::null_mut();
4255        sys_set_robust_list(&mut slot.robust_list, 24);
4256    }
4257}
4258
4259unsafe fn mutex_trylock(m: *mut pthread_mutex_t) -> c_int {
4260    let type_ = (*m).__i[0];
4261    // Fast path: plain NORMAL only (no PI, no robust, no pshared).
4262    // A failed CAS must report EBUSY unconditionally: the old value may be
4263    // the waiter bit (0x80000000), whose low bits are zero, and masking it
4264    // with EBUSY would falsely report that the lock was acquired.
4265    if (type_ & 0x8f) == PTHREAD_MUTEX_NORMAL {
4266        let old = a_cas(&raw mut (*m).__i[1], 0, EBUSY);
4267        return if old == 0 { 0 } else { EBUSY };
4268    }
4269    let base_type = type_ & MUTEX_TYPE_MASK;
4270    let self_tid = sys_gettid() as c_int;
4271    let old = a_load(&raw const (*m).__i[1]);
4272    let own = old & 0x3fffffff;
4273    if own == self_tid {
4274        if (type_ & MUTEX_PI) != 0 && (*m).__i[5] < 0 {
4275            (*m).__i[5] = 0;
4276            return 0;
4277        }
4278        if base_type == PTHREAD_MUTEX_RECURSIVE {
4279            if (*m).__i[5] >= c_int::MAX { return EAGAIN; }
4280            (*m).__i[5] += 1;
4281            return 0;
4282        }
4283        if base_type == PTHREAD_MUTEX_ERRORCHECK { return EDEADLK; }
4284        return EBUSY;
4285    }
4286    if own == 0x3fffffff { return ENOTRECOVERABLE; }
4287    if own != 0 || (old != 0 && (type_ & MUTEX_ROBUST) == 0) { return EBUSY; }
4288    let mut newtid = self_tid;
4289    if (type_ & MUTEX_ROBUST) != 0 {
4290        if (*m).__i[2] != 0 { newtid |= 1i32 << 31; }
4291        newtid |= old & 0x40000000;
4292    }
4293    if a_cas(&raw mut (*m).__i[1], old, newtid) == old {
4294        (*m).__i[5] = 0;
4295        if (type_ & MUTEX_ROBUST) != 0 {
4296            if let Some(slot) = find_thread() {
4297                ensure_robust_list(slot);
4298                let m_next = &raw mut (*m).__i[8] as *mut robust_list;
4299                let head = &mut slot.robust_list.list;
4300                (*m_next).next = head.next;
4301                head.next = m_next;
4302            }
4303        }
4304        if old != 0 { return EOWNERDEAD; }
4305        0
4306    } else { EBUSY }
4307}
4308
4309unsafe fn mutex_lock_internal(m: *mut pthread_mutex_t, abs_timeout: *const timespec) -> c_int {
4310    let type_ = (*m).__i[0];
4311    if (type_ & 0x8f) == PTHREAD_MUTEX_NORMAL && a_cas(&raw mut (*m).__i[1], 0, EBUSY) == 0 {
4312        return 0;
4313    }
4314    let r = mutex_trylock(m);
4315    if r != EBUSY { return r; }
4316    if (type_ & MUTEX_PI) != 0 {
4317        let timeout_ptr = if abs_timeout.is_null() { core::ptr::null() } else { abs_timeout };
4318        loop {
4319            let e = futex_lock_pi(&raw mut (*m).__i[1], timeout_ptr);
4320            match e {
4321                0 => {
4322                    if (type_ & MUTEX_ROBUST) != 0 && (((*m).__i[1] & 0x40000000) != 0 || (*m).__i[2] != 0) {
4323                        (*m).__i[5] = -1;
4324                        let _ = mutex_trylock(m);
4325                        return EOWNERDEAD;
4326                    }
4327                    if (type_ & MUTEX_ROBUST) == 0 && (((*m).__i[1] & 0x40000000) != 0 || (*m).__i[2] != 0) {
4328                        (*m).__i[2] = -1;
4329                        futex_unlock_pi(&raw mut (*m).__i[1]);
4330                        return EBUSY;
4331                    }
4332                    (*m).__i[5] = -1;
4333                    let r2 = mutex_trylock(m);
4334                    if r2 == 0 { return 0; }
4335                    return r2;
4336                }
4337                EINTR => continue,
4338                EDEADLK => {
4339                    if (type_ & MUTEX_TYPE_MASK) == PTHREAD_MUTEX_ERRORCHECK { return e; }
4340                    loop { pause(); }
4341                }
4342                EOWNERDEAD => {
4343                    (*m).__i[5] = -1;
4344                    let _ = mutex_trylock(m);
4345                    return EOWNERDEAD;
4346                }
4347                _ => return e,
4348            }
4349        }
4350    }
4351    let mut spins = 100;
4352    while spins > 0 {
4353        let r = mutex_trylock(m);
4354        if r != EBUSY { return r; }
4355        core::hint::spin_loop();
4356        spins -= 1;
4357    }
4358    loop {
4359        let r = mutex_trylock(m);
4360        if r != EBUSY { return r; }
4361        if (type_ & MUTEX_TYPE_MASK) == PTHREAD_MUTEX_ERRORCHECK {
4362            let self_tid = sys_gettid() as c_int;
4363            if (a_load(&raw const (*m).__i[1]) & 0x3fffffff) == self_tid { return EDEADLK; }
4364        }
4365        a_fetch_add(&raw mut (*m).__i[2], 1);
4366        let val = a_load(&raw const (*m).__i[1]);
4367        // If the previous owner released the lock between trylock() and the
4368        // load above, retry instead of publishing a waiter-only lock word
4369        // (0x80000000) that no thread owns.
4370        if val == 0 {
4371            a_fetch_sub(&raw mut (*m).__i[2], 1);
4372            continue;
4373        }
4374        a_cas(&raw mut (*m).__i[1], val, val | (1i32 << 31));
4375        let e = futex_timedwait(&raw mut (*m).__i[1], val | (1i32 << 31), abs_timeout);
4376        a_fetch_sub(&raw mut (*m).__i[2], 1);
4377        if e != 0 && e != EINTR { return e; }
4378    }
4379}
4380
4381#[no_mangle]
4382pub unsafe extern "C" fn pthread_mutex_init(mutex: *mut pthread_mutex_t, attr: *const pthread_mutexattr_t) -> c_int {
4383    core::ptr::write_bytes(mutex, 0, 1);
4384    if !attr.is_null() { (*mutex).__i[0] = (*attr).__attr as c_int; }
4385    0
4386}
4387#[no_mangle]
4388pub unsafe extern "C" fn pthread_mutex_destroy(_mutex: *mut pthread_mutex_t) -> c_int { 0 }
4389#[no_mangle]
4390pub unsafe extern "C" fn pthread_mutex_lock(mutex: *mut pthread_mutex_t) -> c_int {
4391    mutex_lock_internal(mutex, core::ptr::null())
4392}
4393#[no_mangle]
4394pub unsafe extern "C" fn pthread_mutex_trylock(mutex: *mut pthread_mutex_t) -> c_int {
4395    mutex_trylock(mutex)
4396}
4397#[no_mangle]
4398pub unsafe extern "C" fn pthread_mutex_timedlock(mutex: *mut pthread_mutex_t, abs_timeout: *const timespec) -> c_int {
4399    mutex_lock_internal(mutex, abs_timeout)
4400}
4401#[no_mangle]
4402pub unsafe extern "C" fn pthread_mutex_unlock(mutex: *mut pthread_mutex_t) -> c_int {
4403    let type_ = (*mutex).__i[0];
4404    let base_type = type_ & MUTEX_TYPE_MASK;
4405    let old = a_load(&raw const (*mutex).__i[1]);
4406    if base_type != PTHREAD_MUTEX_NORMAL {
4407        let self_tid = sys_gettid() as c_int;
4408        if (old & 0x3fffffff) != self_tid { return EPERM; }
4409        if base_type == PTHREAD_MUTEX_RECURSIVE && (*mutex).__i[5] > 0 {
4410            (*mutex).__i[5] -= 1;
4411            return 0;
4412        }
4413    }
4414    let mut new = 0;
4415    if (type_ & MUTEX_ROBUST) != 0 && (old & 0x40000000) != 0 {
4416        new = 0x7fffffff;
4417    }
4418    if (type_ & MUTEX_PI) != 0 {
4419        if old < 0 || a_cas(&raw mut (*mutex).__i[1], old, new) != old {
4420            if new != 0 { a_store(&raw mut (*mutex).__i[2], -1); }
4421            return futex_unlock_pi(&raw mut (*mutex).__i[1]);
4422        }
4423        return 0;
4424    }
4425    a_store(&raw mut (*mutex).__i[1], new);
4426    if new == 0 && (a_load(&raw const (*mutex).__i[2]) > 0 || old < 0) {
4427        futex_wake(&raw mut (*mutex).__i[1], 1);
4428    }
4429    0
4430}
4431
4432// --- pthread_condattr_* ---
4433#[no_mangle]
4434pub unsafe extern "C" fn pthread_condattr_init(attr: *mut pthread_condattr_t) -> c_int {
4435    (*attr).__attr = 0; 0
4436}
4437#[no_mangle]
4438pub unsafe extern "C" fn pthread_condattr_destroy(_attr: *mut pthread_condattr_t) -> c_int { 0 }
4439#[no_mangle]
4440pub unsafe extern "C" fn pthread_condattr_setclock(attr: *mut pthread_condattr_t, clk: c_int) -> c_int {
4441    if clk != CLOCK_REALTIME && clk != CLOCK_MONOTONIC { return EINVAL; }
4442    (*attr).__attr = ((*attr).__attr & 0x80000000) | clk as c_uint; 0
4443}
4444#[no_mangle]
4445pub unsafe extern "C" fn pthread_condattr_getclock(attr: *const pthread_condattr_t, clk: *mut c_int) -> c_int {
4446    *clk = ((*attr).__attr & 0x7fffffff) as c_int; 0
4447}
4448#[no_mangle]
4449pub unsafe extern "C" fn pthread_condattr_setpshared(attr: *mut pthread_condattr_t, p: c_int) -> c_int {
4450    if (p as c_uint) > 1 { return EINVAL; }
4451    (*attr).__attr = ((*attr).__attr & 0x7fffffff) | ((p as c_uint) << 31); 0
4452}
4453#[no_mangle]
4454pub unsafe extern "C" fn pthread_condattr_getpshared(attr: *const pthread_condattr_t, p: *mut c_int) -> c_int {
4455    *p = ((*attr).__attr >> 31) as c_int; 0
4456}
4457
4458// --- pthread_cond_* ---
4459#[no_mangle]
4460pub unsafe extern "C" fn pthread_cond_init(cond: *mut pthread_cond_t, attr: *const pthread_condattr_t) -> c_int {
4461    core::ptr::write_bytes(cond, 0, 1);
4462    if !attr.is_null() {
4463        (*cond).__i[4] = ((*attr).__attr & 0x7fffffff) as c_int;
4464    }
4465    0
4466}
4467#[no_mangle]
4468pub unsafe extern "C" fn pthread_cond_destroy(_cond: *mut pthread_cond_t) -> c_int { 0 }
4469#[no_mangle]
4470pub unsafe extern "C" fn pthread_cond_wait(cond: *mut pthread_cond_t, mutex: *mut pthread_mutex_t) -> c_int {
4471    pthread_cond_timedwait(cond, mutex, core::ptr::null())
4472}
4473
4474
4475#[no_mangle]
4476pub unsafe extern "C" fn pthread_cond_timedwait(cond: *mut pthread_cond_t, mutex: *mut pthread_mutex_t, abs_timeout: *const timespec) -> c_int {
4477    pthread_testcancel();
4478    let seq_ptr = &raw mut (*cond).__i[2];
4479    let waiters_ptr = &raw mut (*cond).__i[3];
4480    let seq = a_load(seq_ptr);
4481    a_fetch_add(waiters_ptr, 1);
4482    pthread_mutex_unlock(mutex);
4483    let e = futex_timedwait(seq_ptr, seq, abs_timeout);
4484    a_fetch_sub(waiters_ptr, 1);
4485    let r = pthread_mutex_lock(mutex);
4486    if r != 0 { return r; }
4487    if e == EINTR {
4488        pthread_testcancel();
4489    }
4490    if e != 0 && e != EINTR { return e; }
4491    0
4492}
4493#[no_mangle]
4494pub unsafe extern "C" fn pthread_cond_signal(cond: *mut pthread_cond_t) -> c_int {
4495    if a_load(&raw const (*cond).__i[3]) == 0 { return 0; }
4496    a_fetch_add(&raw mut (*cond).__i[2], 1);
4497    futex_wake(&raw mut (*cond).__i[2], 1);
4498    0
4499}
4500#[no_mangle]
4501pub unsafe extern "C" fn pthread_cond_broadcast(cond: *mut pthread_cond_t) -> c_int {
4502    if a_load(&raw const (*cond).__i[3]) == 0 { return 0; }
4503    a_fetch_add(&raw mut (*cond).__i[2], 1);
4504    futex_wake(&raw mut (*cond).__i[2], -1);
4505    0
4506}
4507
4508// --- pthread_rwlockattr_* ---
4509#[no_mangle]
4510pub unsafe extern "C" fn pthread_rwlockattr_init(attr: *mut pthread_rwlockattr_t) -> c_int {
4511    (*attr).__attr = [0; 2]; 0
4512}
4513#[no_mangle]
4514pub unsafe extern "C" fn pthread_rwlockattr_destroy(_attr: *mut pthread_rwlockattr_t) -> c_int { 0 }
4515#[no_mangle]
4516pub unsafe extern "C" fn pthread_rwlockattr_setpshared(attr: *mut pthread_rwlockattr_t, p: c_int) -> c_int {
4517    if (p as c_uint) > 1 { return EINVAL; }
4518    (*attr).__attr[0] = ((*attr).__attr[0] & !128) | ((p as c_uint) << 7); 0
4519}
4520#[no_mangle]
4521pub unsafe extern "C" fn pthread_rwlockattr_getpshared(attr: *const pthread_rwlockattr_t, p: *mut c_int) -> c_int {
4522    *p = (((*attr).__attr[0] >> 7) & 1) as c_int; 0
4523}
4524
4525// --- pthread_rwlock_* ---
4526unsafe fn rwlock_tryrdlock(rw: *mut pthread_rwlock_t) -> c_int {
4527    loop {
4528        let val = a_load(&raw const (*rw).__i[0]);
4529        let cnt = val & 0x7fffffff;
4530        if cnt == 0x7fffffff { return EBUSY; }
4531        if cnt == 0x7ffffffe { return EAGAIN; }
4532        if a_cas(&raw mut (*rw).__i[0], val, val + 1) == val { return 0; }
4533    }
4534}
4535unsafe fn rwlock_trywrlock(rw: *mut pthread_rwlock_t) -> c_int {
4536    if a_cas(&raw mut (*rw).__i[0], 0, 0x7fffffff) == 0 { 0 } else { EBUSY }
4537}
4538unsafe fn rwlock_timedrdlock(rw: *mut pthread_rwlock_t, abs_timeout: *const timespec) -> c_int {
4539    let r = rwlock_tryrdlock(rw);
4540    if r != EBUSY { return r; }
4541    let mut spins = 100;
4542    while spins > 0 { let r = rwlock_tryrdlock(rw); if r != EBUSY { return r; } core::hint::spin_loop(); spins -= 1; }
4543    loop {
4544        let r = rwlock_tryrdlock(rw);
4545        if r != EBUSY { return r; }
4546        let val = a_load(&raw const (*rw).__i[0]);
4547        if val & 0x7fffffff != 0x7fffffff { continue; }
4548        a_fetch_add(&raw mut (*rw).__i[1], 1);
4549        a_cas(&raw mut (*rw).__i[0], val, val | (1i32 << 31));
4550        let e = futex_timedwait(&raw mut (*rw).__i[0], val | (1i32 << 31), abs_timeout);
4551        a_fetch_sub(&raw mut (*rw).__i[1], 1);
4552        if e != 0 && e != EINTR { return e; }
4553    }
4554}
4555unsafe fn rwlock_timedwrlock(rw: *mut pthread_rwlock_t, abs_timeout: *const timespec) -> c_int {
4556    let r = rwlock_trywrlock(rw);
4557    if r != EBUSY { return r; }
4558    let mut spins = 100;
4559    while spins > 0 { let r = rwlock_trywrlock(rw); if r != EBUSY { return r; } core::hint::spin_loop(); spins -= 1; }
4560    loop {
4561        let r = rwlock_trywrlock(rw);
4562        if r != EBUSY { return r; }
4563        let val = a_load(&raw const (*rw).__i[0]);
4564        if val == 0 { continue; }
4565        a_fetch_add(&raw mut (*rw).__i[1], 1);
4566        a_cas(&raw mut (*rw).__i[0], val, val | (1i32 << 31));
4567        let e = futex_timedwait(&raw mut (*rw).__i[0], val | (1i32 << 31), abs_timeout);
4568        a_fetch_sub(&raw mut (*rw).__i[1], 1);
4569        if e != 0 && e != EINTR { return e; }
4570    }
4571}
4572
4573#[no_mangle]
4574pub unsafe extern "C" fn pthread_rwlock_init(rw: *mut pthread_rwlock_t, attr: *const pthread_rwlockattr_t) -> c_int {
4575    core::ptr::write_bytes(rw, 0, 1);
4576    if !attr.is_null() { (*rw).__i[2] = (*attr).__attr[0] as c_int & 128; }
4577    0
4578}
4579#[no_mangle]
4580pub unsafe extern "C" fn pthread_rwlock_destroy(_rw: *mut pthread_rwlock_t) -> c_int { 0 }
4581#[no_mangle]
4582pub unsafe extern "C" fn pthread_rwlock_rdlock(rw: *mut pthread_rwlock_t) -> c_int { rwlock_timedrdlock(rw, core::ptr::null()) }
4583#[no_mangle]
4584pub unsafe extern "C" fn pthread_rwlock_tryrdlock(rw: *mut pthread_rwlock_t) -> c_int { rwlock_tryrdlock(rw) }
4585#[no_mangle]
4586pub unsafe extern "C" fn pthread_rwlock_timedrdlock(rw: *mut pthread_rwlock_t, t: *const timespec) -> c_int { rwlock_timedrdlock(rw, t) }
4587#[no_mangle]
4588pub unsafe extern "C" fn pthread_rwlock_wrlock(rw: *mut pthread_rwlock_t) -> c_int { rwlock_timedwrlock(rw, core::ptr::null()) }
4589#[no_mangle]
4590pub unsafe extern "C" fn pthread_rwlock_trywrlock(rw: *mut pthread_rwlock_t) -> c_int { rwlock_trywrlock(rw) }
4591#[no_mangle]
4592pub unsafe extern "C" fn pthread_rwlock_timedwrlock(rw: *mut pthread_rwlock_t, t: *const timespec) -> c_int { rwlock_timedwrlock(rw, t) }
4593#[no_mangle]
4594pub unsafe extern "C" fn pthread_rwlock_unlock(rw: *mut pthread_rwlock_t) -> c_int {
4595    loop {
4596        let val = a_load(&raw const (*rw).__i[0]);
4597        let cnt = val & 0x7fffffff;
4598        let new_val = if cnt == 0x7fffffff || cnt == 1 { 0 } else { val - 1 };
4599        if a_cas(&raw mut (*rw).__i[0], val, new_val) == val {
4600            if new_val == 0 && (a_load(&raw const (*rw).__i[1]) > 0 || val < 0) {
4601                futex_wake(&raw mut (*rw).__i[0], cnt);
4602            }
4603            return 0;
4604        }
4605    }
4606}
4607
4608// --- pthread_barrierattr_* ---
4609#[no_mangle]
4610pub unsafe extern "C" fn pthread_barrierattr_init(attr: *mut pthread_barrierattr_t) -> c_int { (*attr).__attr = 0; 0 }
4611#[no_mangle]
4612pub unsafe extern "C" fn pthread_barrierattr_destroy(_attr: *mut pthread_barrierattr_t) -> c_int { 0 }
4613#[no_mangle]
4614pub unsafe extern "C" fn pthread_barrierattr_setpshared(attr: *mut pthread_barrierattr_t, p: c_int) -> c_int {
4615    if (p as c_uint) > 1 { return EINVAL; }
4616    (*attr).__attr = p as c_uint; 0
4617}
4618#[no_mangle]
4619pub unsafe extern "C" fn pthread_barrierattr_getpshared(attr: *const pthread_barrierattr_t, p: *mut c_int) -> c_int {
4620    *p = (*attr).__attr as c_int; 0
4621}
4622
4623// --- pthread_barrier_* ---
4624#[no_mangle]
4625pub unsafe extern "C" fn pthread_barrier_init(b: *mut pthread_barrier_t, _attr: *const pthread_barrierattr_t, count: c_uint) -> c_int {
4626    if count == 0 { return EINVAL; }
4627    core::ptr::write_bytes(b, 0, 1);
4628    (*b).__i[2] = count as c_int - 1;
4629    0
4630}
4631#[no_mangle]
4632pub unsafe extern "C" fn pthread_barrier_destroy(_b: *mut pthread_barrier_t) -> c_int { 0 }
4633#[no_mangle]
4634pub unsafe extern "C" fn pthread_barrier_wait(b: *mut pthread_barrier_t) -> c_int {
4635    let limit = (*b).__i[2];
4636    if limit == 0 { return PTHREAD_BARRIER_SERIAL_THREAD; }
4637    spinlock_lock(&raw mut (*b).__i[0], &raw mut (*b).__i[1]);
4638    let arrived = a_load(&raw const (*b).__i[3]) + 1;
4639    a_store(&raw mut (*b).__i[3], arrived);
4640    if arrived > limit {
4641        a_store(&raw mut (*b).__i[3], 0);
4642        let gen = a_load(&raw const (*b).__i[4]);
4643        a_store(&raw mut (*b).__i[4], gen + 1);
4644        spinlock_unlock(&raw mut (*b).__i[0], &raw mut (*b).__i[1]);
4645        futex_wake(&raw mut (*b).__i[4], -1);
4646        return PTHREAD_BARRIER_SERIAL_THREAD;
4647    }
4648    let gen = a_load(&raw const (*b).__i[4]);
4649    spinlock_unlock(&raw mut (*b).__i[0], &raw mut (*b).__i[1]);
4650    while a_load(&raw const (*b).__i[4]) == gen {
4651        futex_wait(&raw mut (*b).__i[4], gen);
4652    }
4653    0
4654}
4655
4656// --- pthread_spin_* ---
4657#[no_mangle]
4658pub unsafe extern "C" fn pthread_spin_init(s: *mut pthread_spinlock_t, _pshared: c_int) -> c_int { a_store(s, 0); 0 }
4659#[no_mangle]
4660pub unsafe extern "C" fn pthread_spin_destroy(_s: *mut pthread_spinlock_t) -> c_int { 0 }
4661#[no_mangle]
4662pub unsafe extern "C" fn pthread_spin_lock(s: *mut pthread_spinlock_t) -> c_int {
4663    while a_load(s) != 0 || a_cas(s, 0, EBUSY) != 0 { core::hint::spin_loop(); }
4664    0
4665}
4666#[no_mangle]
4667pub unsafe extern "C" fn pthread_spin_trylock(s: *mut pthread_spinlock_t) -> c_int { a_cas(s, 0, EBUSY) }
4668#[no_mangle]
4669pub unsafe extern "C" fn pthread_spin_unlock(s: *mut pthread_spinlock_t) -> c_int { a_store(s, 0); 0 }
4670
4671// --- sem_* ---
4672#[no_mangle]
4673pub unsafe extern "C" fn sem_init(sem: *mut sem_t, pshared: c_int, value: c_uint) -> c_int {
4674    if value > SEM_VALUE_MAX as c_uint { return EINVAL; }
4675    (*sem).__val[0] = value as c_int;
4676    (*sem).__val[1] = 0;
4677    (*sem).__val[2] = if pshared != 0 { 0 } else { 128 };
4678    0
4679}
4680#[no_mangle]
4681pub unsafe extern "C" fn sem_destroy(_sem: *mut sem_t) -> c_int { 0 }
4682
4683unsafe fn sem_trywait_internal(sem: *mut sem_t) -> c_int {
4684    loop {
4685        let val = a_load(&raw const (*sem).__val[0]);
4686        if val & SEM_VALUE_MAX == 0 { return EAGAIN; }
4687        if a_cas(&raw mut (*sem).__val[0], val, val - 1) == val { return 0; }
4688    }
4689}
4690
4691#[no_mangle]
4692pub unsafe extern "C" fn sem_trywait(sem: *mut sem_t) -> c_int {
4693    if sem_trywait_internal(sem) == 0 { 0 } else { ERRNO!() = EAGAIN; -1 }
4694}
4695#[no_mangle]
4696pub unsafe extern "C" fn sem_wait(sem: *mut sem_t) -> c_int {
4697    pthread_testcancel();
4698    if sem_trywait_internal(sem) == 0 { return 0; }
4699    let mut spins = 100;
4700    while spins > 0 { if sem_trywait_internal(sem) == 0 { return 0; } core::hint::spin_loop(); spins -= 1; }
4701    loop {
4702        pthread_testcancel();
4703        if sem_trywait_internal(sem) == 0 { return 0; }
4704        let val = a_load(&raw const (*sem).__val[0]);
4705        if val & SEM_VALUE_MAX != 0 { continue; }
4706        a_cas(&raw mut (*sem).__val[0], val, val | (1i32 << 31));
4707        a_fetch_add(&raw mut (*sem).__val[1], 1);
4708        futex_wait(&raw mut (*sem).__val[0], 1i32 << 31);
4709        a_fetch_sub(&raw mut (*sem).__val[1], 1);
4710    }
4711}
4712#[no_mangle]
4713pub unsafe extern "C" fn sem_timedwait(sem: *mut sem_t, abs_timeout: *const timespec) -> c_int {
4714    pthread_testcancel();
4715    if sem_trywait_internal(sem) == 0 { return 0; }
4716    let mut spins = 100;
4717    while spins > 0 { if sem_trywait_internal(sem) == 0 { return 0; } core::hint::spin_loop(); spins -= 1; }
4718    loop {
4719        pthread_testcancel();
4720        if sem_trywait_internal(sem) == 0 { return 0; }
4721        let val = a_load(&raw const (*sem).__val[0]);
4722        if val & SEM_VALUE_MAX != 0 { continue; }
4723        a_cas(&raw mut (*sem).__val[0], val, val | (1i32 << 31));
4724        a_fetch_add(&raw mut (*sem).__val[1], 1);
4725        let e = futex_timedwait(&raw mut (*sem).__val[0], 1i32 << 31, abs_timeout);
4726        a_fetch_sub(&raw mut (*sem).__val[1], 1);
4727        if e != 0 && e != EINTR { ERRNO!() = e; return -1; }
4728    }
4729}
4730#[no_mangle]
4731pub unsafe extern "C" fn sem_post(sem: *mut sem_t) -> c_int {
4732    loop {
4733        let val = a_load(&raw const (*sem).__val[0]);
4734        if (val & SEM_VALUE_MAX) == SEM_VALUE_MAX { ERRNO!() = EOVERFLOW; return -1; }
4735        let mut new = val + 1;
4736        let waiters = a_load(&raw const (*sem).__val[1]);
4737        if waiters <= 1 { new &= !(1i32 << 31); }
4738        if a_cas(&raw mut (*sem).__val[0], val, new) == val {
4739            if val < 0 || waiters > 0 {
4740                futex_wake(&raw mut (*sem).__val[0], if waiters > 1 { 1 } else { -1 });
4741            }
4742            return 0;
4743        }
4744    }
4745}
4746#[no_mangle]
4747pub unsafe extern "C" fn sem_getvalue(sem: *mut sem_t, sval: *mut c_int) -> c_int {
4748    *sval = a_load(&raw const (*sem).__val[0]) & SEM_VALUE_MAX;
4749    0
4750}
4751
4752// ============================================================
4753// POSIX shared memory + named semaphores
4754// ============================================================
4755
4756const NAME_MAX: usize = 255;
4757const SEM_NSEMS_MAX: usize = 256;
4758
4759// ponytail: global spinlock for semtab, upgrade to per-entry if contention matters
4760static SEMTAB_LOCK: AtomicI32 = AtomicI32::new(0);
4761
4762struct SemEntry {
4763    ino: u64,
4764    sem: *mut sem_t,
4765    refcnt: u32,
4766}
4767
4768// ponytail: zero-init is fine for static mut (all-zero = free slots)
4769static mut SEMTAB: [SemEntry; SEM_NSEMS_MAX] = {
4770    const ZERO: SemEntry = SemEntry { ino: 0, sem: core::ptr::null_mut(), refcnt: 0 };
4771    [ZERO; SEM_NSEMS_MAX]
4772};
4773
4774unsafe fn semtab_lock() {
4775    while SEMTAB_LOCK.compare_exchange_weak(0, 1, Ordering::Acquire, Ordering::Relaxed).is_err() {
4776        core::hint::spin_loop();
4777    }
4778}
4779
4780unsafe fn semtab_unlock() {
4781    SEMTAB_LOCK.store(0, Ordering::Release);
4782}
4783
4784// Validate shm/sem name and construct /dev/shm/<name> path.
4785// Returns path length or -1 with ERRNO set.
4786unsafe fn shm_mapname(name: *const c_char, buf: *mut u8) -> c_int {
4787    let mut p = name;
4788    // strip leading /
4789    while *p == b'/' as c_char { p = p.add(1); }
4790    let start = p;
4791    // find end of first component
4792    while *p != 0 && *p != b'/' as c_char { p = p.add(1); }
4793    let len = p.offset_from(start) as usize;
4794    // no trailing /, not empty, not "." or ".."
4795    if len == 0 || *p != 0 {
4796        ERRNO!() = EINVAL;
4797        return -1;
4798    }
4799    if len == 1 && *start == b'.' as c_char {
4800        ERRNO!() = EINVAL;
4801        return -1;
4802    }
4803    if len == 2 && *start == b'.' as c_char && *start.add(1) == b'.' as c_char {
4804        ERRNO!() = EINVAL;
4805        return -1;
4806    }
4807    if len > NAME_MAX {
4808        ERRNO!() = ENAMETOOLONG;
4809        return -1;
4810    }
4811    // build /dev/shm/<name>
4812    let prefix = b"/dev/shm/\0";
4813    let mut i = 0;
4814    while prefix[i] != 0 {
4815        *buf.add(i) = prefix[i];
4816        i += 1;
4817    }
4818    let mut j = 0;
4819    while j < len {
4820        *buf.add(i + j) = *start.add(j) as u8;
4821        j += 1;
4822    }
4823    *buf.add(i + j) = 0;
4824    (i + j) as c_int
4825}
4826
4827#[no_mangle]
4828pub unsafe extern "C" fn shm_open(name: *const c_char, flags: c_int, mode: mode_t) -> c_int {
4829    let mut buf = [0u8; 16 + NAME_MAX + 1];
4830    if shm_mapname(name, buf.as_mut_ptr()) < 0 { return -1; }
4831    let fd = sys_open(buf.as_ptr(), (flags | O_NOFOLLOW | O_CLOEXEC | O_NONBLOCK) as i64, mode as i64);
4832    if fd < 0 { ERRNO!() = (-fd) as c_int; return -1; }
4833    fd as c_int
4834}
4835
4836#[no_mangle]
4837pub unsafe extern "C" fn shm_unlink(name: *const c_char) -> c_int {
4838    let mut buf = [0u8; 16 + NAME_MAX + 1];
4839    if shm_mapname(name, buf.as_mut_ptr()) < 0 { return -1; }
4840    let r = sys_unlink(buf.as_ptr());
4841    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
4842    0
4843}
4844
4845#[no_mangle]
4846pub unsafe extern "C" fn sem_open(name: *const c_char, flags: c_int, mut args: ...) -> *mut sem_t {
4847    let mut buf = [0u8; 16 + NAME_MAX + 1];
4848    if shm_mapname(name, buf.as_mut_ptr()).is_negative() {
4849        return core::ptr::null_mut(); // SEM_FAILED
4850    }
4851
4852    let oflags = flags & (O_CREAT | O_EXCL);
4853
4854    semtab_lock();
4855
4856    // find a free slot
4857    let mut slot: c_int = -1;
4858    for i in 0..SEMTAB_LEN {
4859        if SEMTAB[i].sem.is_null() && slot < 0 { slot = i as c_int; }
4860    }
4861    if slot < 0 {
4862        ERRNO!() = EMFILE;
4863        semtab_unlock();
4864        return core::ptr::null_mut();
4865    }
4866    // reserve slot
4867    SEMTAB[slot as usize].sem = !0usize as *mut sem_t; // sentinel
4868    semtab_unlock();
4869
4870    // extract va_args for O_CREAT
4871    let mut mode: mode_t = 0;
4872    let mut value: c_uint = 0;
4873    if oflags & O_CREAT != 0 {
4874        mode = args.next_arg::<mode_t>() & 0o666;
4875        value = args.next_arg::<c_uint>();
4876        if value > SEM_VALUE_MAX as c_uint {
4877            ERRNO!() = EINVAL;
4878            semtab_lock();
4879            SEMTAB[slot as usize].sem = core::ptr::null_mut();
4880            semtab_unlock();
4881            return core::ptr::null_mut();
4882        }
4883    }
4884
4885    // try opening existing first (unless O_CREAT|O_EXCL)
4886    if oflags != O_CREAT | O_EXCL {
4887        let fd = sys_open(buf.as_ptr(), (O_RDWR | O_NOFOLLOW | O_CLOEXEC | O_NONBLOCK) as i64, 0);
4888        if fd >= 0 {
4889            let map = sys_mmap(
4890                core::ptr::null_mut(),
4891                core::mem::size_of::<sem_t>(),
4892                PROT_READ | PROT_WRITE,
4893                MAP_SHARED,
4894                fd as i32,
4895                0,
4896            );
4897            sys_close(fd);
4898            if map != MMAP_FAILED {
4899                let st_ino = get_ino(fd as c_int, buf.as_ptr());
4900                return sem_register(map as *mut sem_t, st_ino, slot);
4901            }
4902        }
4903        if oflags == 0 {
4904            // no O_CREAT and file doesn't exist
4905            semtab_lock();
4906            SEMTAB[slot as usize].sem = core::ptr::null_mut();
4907            semtab_unlock();
4908            return core::ptr::null_mut();
4909        }
4910    }
4911
4912    // create: write sem_t to temp file, mmap, link
4913    let mut tmp = [0u8; 32];
4914    let tmpname = b"/dev/shm/tmp-XXXXXX\0";
4915    tmp[..tmpname.len()].copy_from_slice(tmpname);
4916    // use clock for unique name
4917    let mut ts: timespec = core::mem::zeroed();
4918    sys_clock_gettime(0, &mut ts);
4919    // append ns to tmp name
4920    let mut n = ts.tv_nsec;
4921    let mut pos = tmpname.len() - 1;
4922    tmp[pos] = 0;
4923    pos -= 1;
4924    loop {
4925        tmp[pos] = b'0' + (n % 10) as u8;
4926        n /= 10;
4927        if n == 0 || pos == 0 { break; }
4928        pos -= 1;
4929    }
4930        // shift to start of number part
4931        let tmp_path = tmp.as_ptr();
4932
4933    let mut newsem: sem_t = core::mem::zeroed();
4934    newsem.__val[0] = value as c_int;
4935    newsem.__val[1] = 0;
4936    newsem.__val[2] = 128; // pshared=0 flag
4937
4938    loop {
4939        let fd = sys_open(tmp_path as *const u8, (O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC | O_NONBLOCK) as i64, mode as i64);
4940        if fd < 0 {
4941            if (-fd) as c_int == EEXIST { continue; }
4942            semtab_lock();
4943            SEMTAB[slot as usize].sem = core::ptr::null_mut();
4944            semtab_unlock();
4945            return core::ptr::null_mut();
4946        }
4947        let written = sys_write(fd, &newsem as *const sem_t as *const u8, core::mem::size_of::<sem_t>());
4948        if written != core::mem::size_of::<sem_t>() as i64 {
4949            sys_close(fd);
4950            sys_unlink(tmp_path);
4951            semtab_lock();
4952            SEMTAB[slot as usize].sem = core::ptr::null_mut();
4953            semtab_unlock();
4954            return core::ptr::null_mut();
4955        }
4956        let map = sys_mmap(
4957            core::ptr::null_mut(),
4958            core::mem::size_of::<sem_t>(),
4959            PROT_READ | PROT_WRITE,
4960            MAP_SHARED,
4961            fd as i32,
4962            0,
4963        );
4964        sys_close(fd);
4965        if map == MMAP_FAILED {
4966            sys_unlink(tmp_path);
4967            semtab_lock();
4968            SEMTAB[slot as usize].sem = core::ptr::null_mut();
4969            semtab_unlock();
4970            return core::ptr::null_mut();
4971        }
4972        // link to final name
4973        let r = sys_linkat(AT_FDCWD, tmp_path, AT_FDCWD, buf.as_ptr(), 0);
4974        let e = if r < 0 { (-r) as c_int } else { 0 };
4975        sys_unlink(tmp_path);
4976        if e == 0 {
4977            let st_ino = get_ino(-1, buf.as_ptr());
4978            return sem_register(map as *mut sem_t, st_ino, slot);
4979        }
4980        sys_munmap(map, core::mem::size_of::<sem_t>());
4981        if e != EEXIST || oflags == O_CREAT | O_EXCL {
4982            ERRNO!() = e;
4983            semtab_lock();
4984            SEMTAB[slot as usize].sem = core::ptr::null_mut();
4985            semtab_unlock();
4986            return core::ptr::null_mut();
4987        }
4988        // EEXIST without O_EXCL: retry open existing
4989        break;
4990    }
4991
4992    // fallthrough: try opening the existing file that won the race
4993    let fd = sys_open(buf.as_ptr(), (O_RDWR | O_NOFOLLOW | O_CLOEXEC | O_NONBLOCK) as i64, 0);
4994    if fd < 0 {
4995        semtab_lock();
4996        SEMTAB[slot as usize].sem = core::ptr::null_mut();
4997        semtab_unlock();
4998        return core::ptr::null_mut();
4999    }
5000    let map = sys_mmap(
5001        core::ptr::null_mut(),
5002        core::mem::size_of::<sem_t>(),
5003        PROT_READ | PROT_WRITE,
5004        MAP_SHARED,
5005        fd as i32,
5006        0,
5007    );
5008    sys_close(fd);
5009    if map == MMAP_FAILED {
5010        semtab_lock();
5011        SEMTAB[slot as usize].sem = core::ptr::null_mut();
5012        semtab_unlock();
5013        return core::ptr::null_mut();
5014    }
5015    let st_ino = get_ino(-1, buf.as_ptr());
5016    sem_register(map as *mut sem_t, st_ino, slot)
5017}
5018
5019unsafe fn get_ino(_fd_hint: c_int, path: *const u8) -> u64 {
5020    let mut st: Stat = core::mem::zeroed();
5021    if sys_newfstatat(AT_FDCWD, path as *const c_char, &mut st as *mut Stat as *mut u8, 0) == 0 {
5022        st.st_ino
5023    } else {
5024        0
5025    }
5026}
5027
5028unsafe fn sem_register(map: *mut sem_t, ino: u64, slot_hint: c_int) -> *mut sem_t {
5029    semtab_lock();
5030    // check if already mapped (by inode)
5031    for i in 0..SEMTAB_LEN {
5032        if SEMTAB[i].ino == ino && !SEMTAB[i].sem.is_null() && SEMTAB[i].sem != !0usize as *mut sem_t {
5033            // already mapped: unmap new, use existing, release hint slot
5034            sys_munmap(map as *mut u8, core::mem::size_of::<sem_t>());
5035            SEMTAB[slot_hint as usize].sem = core::ptr::null_mut();
5036            SEMTAB[i].refcnt += 1;
5037            let ret = SEMTAB[i].sem;
5038            semtab_unlock();
5039            return ret;
5040        }
5041    }
5042    // new entry
5043    SEMTAB[slot_hint as usize].sem = map;
5044    SEMTAB[slot_hint as usize].ino = ino;
5045    SEMTAB[slot_hint as usize].refcnt = 1;
5046    semtab_unlock();
5047    map
5048}
5049
5050const SEMTAB_LEN: usize = SEM_NSEMS_MAX;
5051
5052#[no_mangle]
5053pub unsafe extern "C" fn sem_close(sem: *mut sem_t) -> c_int {
5054    semtab_lock();
5055    for i in 0..SEMTAB_LEN {
5056        if SEMTAB[i].sem == sem {
5057            SEMTAB[i].refcnt -= 1;
5058            if SEMTAB[i].refcnt == 0 {
5059                SEMTAB[i].sem = core::ptr::null_mut();
5060                SEMTAB[i].ino = 0;
5061                semtab_unlock();
5062                sys_munmap(sem as *mut u8, core::mem::size_of::<sem_t>());
5063                return 0;
5064            }
5065            semtab_unlock();
5066            return 0;
5067        }
5068    }
5069    semtab_unlock();
5070    0
5071}
5072
5073#[no_mangle]
5074pub unsafe extern "C" fn sem_unlink(name: *const c_char) -> c_int {
5075    shm_unlink(name)
5076}
5077
5078// --- pthread_once ---
5079#[no_mangle]
5080pub unsafe extern "C" fn pthread_once(control: *mut pthread_once_t, init_routine: Option<unsafe extern "C" fn()>) -> c_int {
5081    if a_load(control) == 2 { return 0; }
5082    loop {
5083        match a_cas(control, 0, 1) {
5084            0 => {
5085                if let Some(f) = init_routine { f(); }
5086                if a_swap(control, 2) == 3 { futex_wake(control, -1); }
5087                return 0;
5088            }
5089            1 => { a_cas(control, 1, 3); }
5090            3 => { futex_wait(control, 3); }
5091            _ => { return 0; }
5092        }
5093    }
5094}
5095
5096// --- pthread_key_* ---
5097#[no_mangle]
5098pub unsafe extern "C" fn pthread_key_create(key: *mut pthread_key_t, dtor: Option<unsafe extern "C" fn(*mut c_void)>) -> c_int {
5099    let start = NEXT_KEY.load(Ordering::Relaxed);
5100    let mut j = start;
5101    loop {
5102        if KEY_DTORS[j].is_none() {
5103            KEY_DTORS[j] = dtor;
5104            NEXT_KEY.store((j + 1) % PTHREAD_KEYS_MAX, Ordering::Relaxed);
5105            *key = j as pthread_key_t;
5106            return 0;
5107        }
5108        j = (j + 1) % PTHREAD_KEYS_MAX;
5109        if j == start { return EAGAIN; }
5110    }
5111}
5112#[no_mangle]
5113pub unsafe extern "C" fn pthread_key_delete(key: pthread_key_t) -> c_int {
5114    KEY_DTORS[key as usize] = None;
5115    for i in 0..MAX_THREADS {
5116        if THREADS[i].tid > 0 { THREADS[i].tsd[key as usize] = core::ptr::null_mut(); }
5117    }
5118    0
5119}
5120#[no_mangle]
5121pub unsafe extern "C" fn pthread_getspecific(key: pthread_key_t) -> *mut c_void {
5122    if let Some(slot) = find_thread() { slot.tsd[key as usize] } else { core::ptr::null_mut() }
5123}
5124#[no_mangle]
5125pub unsafe extern "C" fn pthread_setspecific(key: pthread_key_t, value: *const c_void) -> c_int {
5126    if let Some(slot) = find_thread() {
5127        slot.tsd[key as usize] = value as *mut c_void;
5128        0
5129    } else { EINVAL }
5130}
5131
5132// --- pthread_cancel/state ---
5133unsafe fn run_cleanup_handlers(slot: &mut Thread) {
5134    let mut cb = slot.cancelbuf;
5135    while !cb.is_null() {
5136        let cur = &*cb;
5137        let f = cur.__f;
5138        let x = cur.__x;
5139        cb = cur.__next;
5140        if let Some(func) = f {
5141            func(x);
5142        }
5143    }
5144    slot.cancelbuf = core::ptr::null_mut();
5145}
5146
5147unsafe fn do_cancel() -> ! {
5148    if let Some(slot) = find_thread() {
5149        run_cleanup_handlers(slot);
5150        slot.result = !0usize as *mut c_void; // PTHREAD_CANCELED
5151        slot.detach_state = DT_EXITED;
5152        futex_wake(&raw mut slot.detach_state, 1);
5153    }
5154    sys_exit_thread(0);
5155}
5156
5157extern "C" fn cancel_handler(_sig: c_int) {
5158    unsafe {
5159        if let Some(slot) = find_thread() {
5160            // In asynchronous mode, cancel immediately from the signal handler.
5161            // In deferred mode, the signal just interrupts blocking syscalls so
5162            // the next cancellation point can act on the pending request.
5163            if slot.cancel != 0
5164                && slot.cancel_state == PTHREAD_CANCEL_ENABLE
5165                && slot.cancel_type == PTHREAD_CANCEL_ASYNCHRONOUS
5166            {
5167                do_cancel();
5168            }
5169        }
5170    }
5171}
5172
5173static CANCEL_INIT: AtomicI32 = AtomicI32::new(0);
5174
5175unsafe fn ensure_cancel_handler() {
5176    if CANCEL_INIT.swap(1, Ordering::AcqRel) == 0 {
5177        let act = KSigAction {
5178            sa_handler: cancel_handler as *const () as usize,
5179            sa_flags: SA_RESTORER,
5180            sa_restorer: sig_restorer as *const () as usize,
5181            sa_mask: [!0u64],
5182        };
5183        sys_rt_sigaction(SIGCANCEL, &act as *const KSigAction, core::ptr::null_mut(), 8);
5184    }
5185}
5186
5187#[no_mangle]
5188pub unsafe extern "C" fn pthread_cancel(thread: PthreadT) -> c_int {
5189    let slot = thread as *mut Thread;
5190    if slot.is_null() { return EINVAL; }
5191    ensure_cancel_handler();
5192    a_store(&raw mut (*slot).cancel, 1);
5193    let tid = (*slot).tid;
5194    if tid > 0 {
5195        sys_tgkill(sys_getpid() as c_int, tid, SIGCANCEL);
5196    }
5197    0
5198}
5199#[no_mangle]
5200pub unsafe extern "C" fn pthread_setcancelstate(state: c_int, oldstate: *mut c_int) -> c_int {
5201    if state != PTHREAD_CANCEL_ENABLE && state != PTHREAD_CANCEL_DISABLE { return EINVAL; }
5202    if let Some(slot) = find_thread() {
5203        if !oldstate.is_null() { *oldstate = slot.cancel_state; }
5204        slot.cancel_state = state;
5205        0
5206    } else { EINVAL }
5207}
5208#[no_mangle]
5209pub unsafe extern "C" fn pthread_setcanceltype(type_: c_int, oldtype: *mut c_int) -> c_int {
5210    if type_ != PTHREAD_CANCEL_DEFERRED && type_ != PTHREAD_CANCEL_ASYNCHRONOUS { return EINVAL; }
5211    if let Some(slot) = find_thread() {
5212        if !oldtype.is_null() { *oldtype = slot.cancel_type; }
5213        slot.cancel_type = type_;
5214        0
5215    } else { EINVAL }
5216}
5217#[no_mangle]
5218pub unsafe extern "C" fn pthread_testcancel() {
5219    if let Some(slot) = find_thread() {
5220        if slot.cancel != 0 && slot.cancel_state == PTHREAD_CANCEL_ENABLE {
5221            do_cancel();
5222        }
5223    }
5224}
5225
5226#[no_mangle]
5227pub unsafe extern "C" fn _pthread_cleanup_push(cb: *mut __ptcb, f: Option<unsafe extern "C" fn(*mut c_void)>, x: *mut c_void) {
5228    (*cb).__f = f;
5229    (*cb).__x = x;
5230    if let Some(slot) = find_thread() {
5231        (*cb).__next = slot.cancelbuf;
5232        slot.cancelbuf = cb;
5233    } else {
5234        (*cb).__next = core::ptr::null_mut();
5235    }
5236}
5237
5238#[no_mangle]
5239pub unsafe extern "C" fn _pthread_cleanup_pop(cb: *mut __ptcb, run: c_int) {
5240    if let Some(slot) = find_thread() {
5241        slot.cancelbuf = (*cb).__next;
5242    }
5243    if run != 0 {
5244        if let Some(f) = (*cb).__f {
5245            f((*cb).__x);
5246        }
5247    }
5248}
5249
5250#[no_mangle]
5251pub unsafe extern "C" fn pthread_kill(thread: PthreadT, sig: c_int) -> c_int {
5252    let slot = thread as *mut Thread;
5253    if slot.is_null() { return EINVAL; }
5254    let tid = (*slot).tid;
5255    if tid <= 0 { return EINVAL; }
5256    if sys_tgkill(sys_getpid() as c_int, tid, sig) < 0 { -1 } else { 0 }
5257}
5258
5259#[no_mangle]
5260pub unsafe extern "C" fn pthread_sigmask(how: c_int, set: *const SigSetT, oldset: *mut SigSetT) -> c_int {
5261    sigprocmask(how, set, oldset)
5262}
5263
5264
5265#[repr(C)]
5266pub struct lconv {
5267    pub decimal_point: *mut c_char,
5268    pub thousands_sep: *mut c_char,
5269    pub grouping: *mut c_char,
5270    pub int_curr_symbol: *mut c_char,
5271    pub currency_symbol: *mut c_char,
5272    pub mon_decimal_point: *mut c_char,
5273    pub mon_thousands_sep: *mut c_char,
5274    pub mon_grouping: *mut c_char,
5275    pub positive_sign: *mut c_char,
5276    pub negative_sign: *mut c_char,
5277    pub int_frac_digits: c_char,
5278    pub frac_digits: c_char,
5279    pub p_cs_precedes: c_char,
5280    pub p_sep_by_space: c_char,
5281    pub n_cs_precedes: c_char,
5282    pub n_sep_by_space: c_char,
5283    pub p_sign_posn: c_char,
5284    pub n_sign_posn: c_char,
5285    pub int_p_cs_precedes: c_char,
5286    pub int_p_sep_by_space: c_char,
5287    pub int_n_cs_precedes: c_char,
5288    pub int_n_sep_by_space: c_char,
5289    pub int_p_sign_posn: c_char,
5290    pub int_n_sign_posn: c_char,
5291}
5292
5293static UTF8_NAME: [u8; 6] = [b'U', b'T', b'F', b'-', b'8', 0];
5294static C_NAME: [u8; 2] = [b'C', 0];
5295static mut LOCALE_NAME: *const c_char = C_NAME.as_ptr() as *const c_char;
5296static mut LOCALE_CTYPE_UTF8: bool = true;
5297static mut EMPTY_STR: [c_char; 1] = [0];
5298static mut DECIMAL_POINT: [c_char; 2] = [b'.' as c_char, 0];
5299static mut LCONV: lconv = lconv {
5300    decimal_point: core::ptr::null_mut(),
5301    thousands_sep: core::ptr::null_mut(),
5302    grouping: core::ptr::null_mut(),
5303    int_curr_symbol: core::ptr::null_mut(),
5304    currency_symbol: core::ptr::null_mut(),
5305    mon_decimal_point: core::ptr::null_mut(),
5306    mon_thousands_sep: core::ptr::null_mut(),
5307    mon_grouping: core::ptr::null_mut(),
5308    positive_sign: core::ptr::null_mut(),
5309    negative_sign: core::ptr::null_mut(),
5310    int_frac_digits: (-1i32) as c_char,
5311    frac_digits: (-1i32) as c_char,
5312    p_cs_precedes: (-1i32) as c_char,
5313    p_sep_by_space: (-1i32) as c_char,
5314    n_cs_precedes: (-1i32) as c_char,
5315    n_sep_by_space: (-1i32) as c_char,
5316    p_sign_posn: (-1i32) as c_char,
5317    n_sign_posn: (-1i32) as c_char,
5318    int_p_cs_precedes: (-1i32) as c_char,
5319    int_p_sep_by_space: (-1i32) as c_char,
5320    int_n_cs_precedes: (-1i32) as c_char,
5321    int_n_sep_by_space: (-1i32) as c_char,
5322    int_p_sign_posn: (-1i32) as c_char,
5323    int_n_sign_posn: (-1i32) as c_char,
5324};
5325
5326unsafe fn cstr_contains_ci(s: *const u8, needle: &[u8]) -> bool {
5327    let len = strlen(s as *const c_char) as usize;
5328    let s_slice = core::slice::from_raw_parts(s, len);
5329    if needle.len() > len { return false; }
5330    for i in 0..=len - needle.len() {
5331        if s_slice[i..i + needle.len()].eq_ignore_ascii_case(needle) {
5332            return true;
5333        }
5334    }
5335    false
5336}
5337
5338#[no_mangle]
5339pub unsafe extern "C" fn setlocale(_category: c_int, locale: *const c_char) -> *mut c_char {
5340    if locale.is_null() || *locale == 0 {
5341        return LOCALE_NAME as *mut c_char;
5342    }
5343    let name = locale as *const u8;
5344    if strcmp(name, b"C\0".as_ptr()) == 0 || strcmp(name, b"POSIX\0".as_ptr()) == 0 {
5345        LOCALE_CTYPE_UTF8 = false;
5346        return LOCALE_NAME as *mut c_char;
5347    }
5348    if cstr_contains_ci(name, b"UTF-8") || cstr_contains_ci(name, b"UTF8") {
5349        LOCALE_CTYPE_UTF8 = true;
5350        return LOCALE_NAME as *mut c_char;
5351    }
5352    core::ptr::null_mut()
5353}
5354
5355#[no_mangle]
5356pub unsafe extern "C" fn localeconv() -> *mut lconv {
5357    let empty = core::ptr::addr_of_mut!(EMPTY_STR) as *mut c_char;
5358    let lconv = core::ptr::addr_of_mut!(LCONV);
5359    (*lconv).decimal_point = core::ptr::addr_of_mut!(DECIMAL_POINT) as *mut c_char;
5360    (*lconv).thousands_sep = empty;
5361    (*lconv).grouping = empty;
5362    (*lconv).int_curr_symbol = empty;
5363    (*lconv).currency_symbol = empty;
5364    (*lconv).mon_decimal_point = empty;
5365    (*lconv).mon_thousands_sep = empty;
5366    (*lconv).mon_grouping = empty;
5367    (*lconv).positive_sign = empty;
5368    (*lconv).negative_sign = empty;
5369    lconv
5370}
5371
5372pub type locale_t = *mut c_void;
5373static mut CURRENT_LOCALE: locale_t = LC_GLOBAL_LOCALE;
5374
5375static mut C_LOCALE_STORAGE: c_int = 0;
5376const C_LOCALE: locale_t = core::ptr::addr_of_mut!(C_LOCALE_STORAGE) as locale_t;
5377const LC_GLOBAL_LOCALE: locale_t = usize::MAX as locale_t;
5378
5379#[no_mangle]
5380pub unsafe extern "C" fn newlocale(_mask: c_int, name: *const c_char, base: locale_t) -> locale_t {
5381    if !name.is_null() && *name != 0 {
5382        let n = name as *const u8;
5383        if strcmp(n, b"C\0".as_ptr()) != 0
5384            && strcmp(n, b"POSIX\0".as_ptr()) != 0
5385        {
5386            return core::ptr::null_mut();
5387        }
5388    }
5389    if base.is_null() || base == C_LOCALE || base == LC_GLOBAL_LOCALE {
5390        C_LOCALE
5391    } else {
5392        core::ptr::null_mut()
5393    }
5394}
5395
5396#[no_mangle]
5397pub unsafe extern "C" fn freelocale(_loc: locale_t) {}
5398
5399#[no_mangle]
5400pub unsafe extern "C" fn uselocale(loc: locale_t) -> locale_t {
5401    let old = CURRENT_LOCALE;
5402    if !loc.is_null() {
5403        CURRENT_LOCALE = loc;
5404    }
5405    old
5406}
5407
5408#[no_mangle]
5409pub unsafe extern "C" fn duplocale(loc: locale_t) -> locale_t {
5410    if loc.is_null() {
5411        return core::ptr::null_mut();
5412    }
5413    C_LOCALE
5414}
5415
5416const NL_ITEM_CODESET: c_int = 14;
5417const NL_ITEM_RADIXCHAR: c_int = 0x10000;
5418const NL_ITEM_THOUSEP: c_int = 0x10001;
5419const NL_ITEM_YESEXPR: c_int = 0x50000;
5420const NL_ITEM_NOEXPR: c_int = 0x50001;
5421
5422const C_TIME_STRINGS: &[u8] = b"Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat\0\
5423Sunday\0Monday\0Tuesday\0Wednesday\0Thursday\0Friday\0Saturday\0\
5424Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\0Dec\0\
5425January\0February\0March\0April\0May\0June\0July\0August\0\
5426September\0October\0November\0December\0\
5427AM\0PM\0\
5428%a %b %e %T %Y\0%m/%d/%y\0%H:%M:%S\0%I:%M:%S %p\0\
5429\0\0%m/%d/%y\00123456789\0%a %b %e %T %Y\0%H:%M:%S\0";
5430
5431const C_NUMERIC_STRINGS: &[u8] = b".\0\0";
5432const C_MESSAGES_STRINGS: &[u8] = b"^[yY]\0^[nN]\0";
5433
5434unsafe fn langinfo_str(table: *const u8, mut idx: c_int) -> *mut c_char {
5435    let mut p = table;
5436    while idx > 0 {
5437        while *p != 0 { p = p.add(1); }
5438        p = p.add(1);
5439        idx -= 1;
5440    }
5441    p as *mut c_char
5442}
5443
5444#[no_mangle]
5445pub unsafe extern "C" fn nl_langinfo(item: c_int) -> *mut c_char {
5446    if item == NL_ITEM_CODESET {
5447        return if LOCALE_CTYPE_UTF8 {
5448            b"UTF-8\0".as_ptr() as *mut c_char
5449        } else {
5450            b"US-ASCII\0".as_ptr() as *mut c_char
5451        };
5452    }
5453    let cat = item >> 16;
5454    let idx = item & 0xFFFF;
5455    match cat {
5456        1 => {
5457            if idx > 1 { return b"\0".as_ptr() as *mut c_char; }
5458            langinfo_str(C_NUMERIC_STRINGS.as_ptr(), idx)
5459        }
5460        2 => {
5461            if idx > 49 { return b"\0".as_ptr() as *mut c_char; }
5462            langinfo_str(C_TIME_STRINGS.as_ptr(), idx)
5463        }
5464        5 => {
5465            if idx > 1 { return b"\0".as_ptr() as *mut c_char; }
5466            langinfo_str(C_MESSAGES_STRINGS.as_ptr(), idx)
5467        }
5468        _ => b"\0".as_ptr() as *mut c_char,
5469    }
5470}
5471
5472pub type nl_catd = *mut c_void;
5473
5474#[no_mangle]
5475pub unsafe extern "C" fn catopen(_name: *const c_char, _oflag: c_int) -> nl_catd {
5476    !0usize as nl_catd
5477}
5478
5479#[no_mangle]
5480pub unsafe extern "C" fn catclose(_catd: nl_catd) -> c_int { 0 }
5481
5482#[no_mangle]
5483pub unsafe extern "C" fn catgets(catd: nl_catd, _set_id: c_int, _msg_id: c_int, s: *const c_char) -> *mut c_char {
5484    if catd == !0usize as nl_catd { return s as *mut c_char; }
5485    s as *mut c_char
5486}
5487
5488pub type IconvT = *mut c_void;
5489
5490const ENC_UTF8: i32 = 1;
5491const ENC_UTF16LE: i32 = 2;
5492const ENC_UTF16BE: i32 = 3;
5493const ENC_UTF32LE: i32 = 4;
5494const ENC_UTF32BE: i32 = 5;
5495const ENC_WCHAR_T: i32 = 6;
5496const ENC_ASCII: i32 = 7;
5497const ENC_LATIN1: i32 = 8;
5498const ENC_WIN1252: i32 = 9;
5499const ENC_WIN1251: i32 = 10;
5500const ENC_KOI8R: i32 = 11;
5501const ENC_GBK: i32 = 12;
5502const ENC_GB2312: i32 = 13;
5503const ENC_BIG5: i32 = 14;
5504const ENC_EUCJP: i32 = 15;
5505const ENC_SHIFTJIS: i32 = 16;
5506const ENC_ISO8859_2: i32 = 17;
5507const ENC_ISO8859_3: i32 = 18;
5508const ENC_ISO8859_4: i32 = 19;
5509const ENC_ISO8859_5: i32 = 20;
5510const ENC_ISO8859_6: i32 = 21;
5511const ENC_ISO8859_7: i32 = 22;
5512const ENC_ISO8859_8: i32 = 23;
5513const ENC_ISO8859_9: i32 = 24;
5514const ENC_ISO8859_10: i32 = 25;
5515const ENC_ISO8859_11: i32 = 26;
5516const ENC_ISO8859_13: i32 = 27;
5517const ENC_ISO8859_14: i32 = 28;
5518const ENC_ISO8859_15: i32 = 29;
5519const ENC_ISO8859_16: i32 = 30;
5520
5521include!("iconv_iso8859.rs");
5522
5523fn make_cd(from: i32, to: i32) -> IconvT {
5524    ((from as usize) << 16 | (to as usize) << 1 | 1) as IconvT
5525}
5526fn extract_from(cd: IconvT) -> i32 {
5527    ((cd as usize) >> 16) as i32
5528}
5529fn extract_to(cd: IconvT) -> i32 {
5530    (((cd as usize) >> 1) & 0x7fff) as i32
5531}
5532
5533unsafe fn match_name(input: *const u8, target: &[u8]) -> bool {
5534    let mut i = input;
5535    let mut j = 0;
5536    while *i != 0 && j < target.len() {
5537        let mut c = *i;
5538        if !c.is_ascii_alphanumeric() { i = i.add(1); continue; }
5539        c = c.to_ascii_lowercase();
5540        if c != target[j] { return false; }
5541        i = i.add(1);
5542        j += 1;
5543    }
5544    while *i != 0 && !(*i).is_ascii_alphanumeric() { i = i.add(1); }
5545    *i == 0 && j == target.len()
5546}
5547
5548unsafe fn find_encoding(name: *const u8) -> i32 {
5549    if name.is_null() { return -1; }
5550    if match_name(name, b"utf8") || match_name(name, b"utf-8") || match_name(name, b"char") { return ENC_UTF8; }
5551    if match_name(name, b"utf16le") || match_name(name, b"utf-16le") { return ENC_UTF16LE; }
5552    if match_name(name, b"utf16be") || match_name(name, b"utf-16be") { return ENC_UTF16BE; }
5553    if match_name(name, b"utf32le") || match_name(name, b"utf-32le") || match_name(name, b"ucs4le") { return ENC_UTF32LE; }
5554    if match_name(name, b"utf32be") || match_name(name, b"utf-32be") || match_name(name, b"ucs4be") { return ENC_UTF32BE; }
5555    if match_name(name, b"wchart") || match_name(name, b"wchar-t") { return ENC_WCHAR_T; }
5556    if match_name(name, b"ascii") || match_name(name, b"usascii") || match_name(name, b"iso646") { return ENC_ASCII; }
5557    if match_name(name, b"iso88591") || match_name(name, b"iso-8859-1") || match_name(name, b"latin1") { return ENC_LATIN1; }
5558    if match_name(name, b"iso88592") || match_name(name, b"iso-8859-2") { return ENC_ISO8859_2; }
5559    if match_name(name, b"iso88593") || match_name(name, b"iso-8859-3") { return ENC_ISO8859_3; }
5560    if match_name(name, b"iso88594") || match_name(name, b"iso-8859-4") { return ENC_ISO8859_4; }
5561    if match_name(name, b"iso88595") || match_name(name, b"iso-8859-5") { return ENC_ISO8859_5; }
5562    if match_name(name, b"iso88596") || match_name(name, b"iso-8859-6") { return ENC_ISO8859_6; }
5563    if match_name(name, b"iso88597") || match_name(name, b"iso-8859-7") { return ENC_ISO8859_7; }
5564    if match_name(name, b"iso88598") || match_name(name, b"iso-8859-8") { return ENC_ISO8859_8; }
5565    if match_name(name, b"iso88599") || match_name(name, b"iso-8859-9") { return ENC_ISO8859_9; }
5566    if match_name(name, b"iso885910") || match_name(name, b"iso-8859-10") { return ENC_ISO8859_10; }
5567    if match_name(name, b"iso885911") || match_name(name, b"iso-8859-11") || match_name(name, b"tis620") { return ENC_ISO8859_11; }
5568    if match_name(name, b"iso885913") || match_name(name, b"iso-8859-13") { return ENC_ISO8859_13; }
5569    if match_name(name, b"iso885914") || match_name(name, b"iso-8859-14") { return ENC_ISO8859_14; }
5570    if match_name(name, b"iso885915") || match_name(name, b"iso-8859-15") { return ENC_ISO8859_15; }
5571    if match_name(name, b"iso885916") || match_name(name, b"iso-8859-16") { return ENC_ISO8859_16; }
5572    if match_name(name, b"cp1252") || match_name(name, b"windows1252") || match_name(name, b"windows-1252") { return ENC_WIN1252; }
5573    if match_name(name, b"cp1251") || match_name(name, b"windows1251") || match_name(name, b"windows-1251") { return ENC_WIN1251; }
5574    if match_name(name, b"koi8r") || match_name(name, b"koi8-r") { return ENC_KOI8R; }
5575    if match_name(name, b"gbk") || match_name(name, b"cp936") { return ENC_GBK; }
5576    if match_name(name, b"gb2312") { return ENC_GB2312; }
5577    if match_name(name, b"big5") || match_name(name, b"bigfive") || match_name(name, b"cp950") { return ENC_BIG5; }
5578    if match_name(name, b"eucjp") || match_name(name, b"euc-jp") { return ENC_EUCJP; }
5579    if match_name(name, b"shiftjis") || match_name(name, b"sjis") || match_name(name, b"cp932") { return ENC_SHIFTJIS; }
5580    -1
5581}
5582
5583fn uni_to_jis(c: u32) -> u16 {
5584    let mut b = 0usize;
5585    let mut nel = REV_JIS.len();
5586    loop {
5587        let i = nel / 2;
5588        let j = REV_JIS[b + i] as usize;
5589        let row = j / 256;
5590        let col = j % 256;
5591        let d = JIS0208[row * 94 + col] as u32;
5592        if d == c { return (j as u16) + 0x2121; }
5593        if nel == 1 { return 0; }
5594        if c < d { nel /= 2; }
5595        else { b += i; nel -= nel / 2; }
5596    }
5597}
5598
5599unsafe fn iconv_decode(enc: i32, src: *const u8, src_left: usize) -> (u32, usize, c_int) {
5600    if src_left == 0 { return (0, 0, EINVAL); }
5601    match enc {
5602        ENC_UTF8 => {
5603            let c = *src;
5604            if c < 128 { return (c as u32, 1, 0); }
5605            if c < 0xC2 { return (0, 0, EILSEQ); }
5606            if c < 0xE0 {
5607                if src_left < 2 || (*src.add(1) & 0xC0) != 0x80 { return (0, 0, EILSEQ); }
5608                return (((c as u32 & 0x1F) << 6) | (*src.add(1) as u32 & 0x3F), 2, 0);
5609            }
5610            if c < 0xF0 {
5611                if src_left < 3 || (*src.add(1) & 0xC0) != 0x80 || (*src.add(2) & 0xC0) != 0x80 { return (0, 0, EILSEQ); }
5612                let cp = ((c as u32 & 0x0F) << 12) | ((*src.add(1) as u32 & 0x3F) << 6) | (*src.add(2) as u32 & 0x3F);
5613                if cp < 0x800 { return (0, 0, EILSEQ); }
5614                return (cp, 3, 0);
5615            }
5616            if c < 0xF8 {
5617                if src_left < 4 || (*src.add(1) & 0xC0) != 0x80 || (*src.add(2) & 0xC0) != 0x80 || (*src.add(3) & 0xC0) != 0x80 { return (0, 0, EILSEQ); }
5618                let cp = ((c as u32 & 0x07) << 18) | ((*src.add(1) as u32 & 0x3F) << 12) | ((*src.add(2) as u32 & 0x3F) << 6) | (*src.add(3) as u32 & 0x3F);
5619                if cp < 0x10000 || cp >= 0x110000 { return (0, 0, EILSEQ); }
5620                return (cp, 4, 0);
5621            }
5622            (0, 0, EILSEQ)
5623        }
5624        ENC_UTF16LE => {
5625            if src_left < 2 { return (0, 0, EINVAL); }
5626            let c = (*src as u32) | ((*src.add(1) as u32) << 8);
5627            if c >= 0xD800 && c <= 0xDBFF {
5628                if src_left < 4 { return (0, 0, EINVAL); }
5629                let d = (*src.add(2) as u32) | ((*src.add(3) as u32) << 8);
5630                if d < 0xDC00 || d > 0xDFFF { return (0, 0, EILSEQ); }
5631                return (((c - 0xD800) << 10) + (d - 0xDC00) + 0x10000, 4, 0);
5632            }
5633            if c >= 0xDC00 && c <= 0xDFFF { return (0, 0, EILSEQ); }
5634            (c, 2, 0)
5635        }
5636        ENC_UTF16BE => {
5637            if src_left < 2 { return (0, 0, EINVAL); }
5638            let c = ((*src as u32) << 8) | (*src.add(1) as u32);
5639            if c >= 0xD800 && c <= 0xDBFF {
5640                if src_left < 4 { return (0, 0, EINVAL); }
5641                let d = ((*src.add(2) as u32) << 8) | (*src.add(3) as u32);
5642                if d < 0xDC00 || d > 0xDFFF { return (0, 0, EILSEQ); }
5643                return (((c - 0xD800) << 10) + (d - 0xDC00) + 0x10000, 4, 0);
5644            }
5645            if c >= 0xDC00 && c <= 0xDFFF { return (0, 0, EILSEQ); }
5646            (c, 2, 0)
5647        }
5648        ENC_UTF32LE => {
5649            if src_left < 4 { return (0, 0, EINVAL); }
5650            let c = (*src as u32) | ((*src.add(1) as u32) << 8) | ((*src.add(2) as u32) << 16) | ((*src.add(3) as u32) << 24);
5651            if c >= 0xD800 && c < 0xE000 { return (0, 0, EILSEQ); }
5652            if c >= 0x110000 { return (0, 0, EILSEQ); }
5653            (c, 4, 0)
5654        }
5655        ENC_UTF32BE => {
5656            if src_left < 4 { return (0, 0, EINVAL); }
5657            let c = ((*src as u32) << 24) | ((*src.add(1) as u32) << 16) | ((*src.add(2) as u32) << 8) | (*src.add(3) as u32);
5658            if c >= 0xD800 && c < 0xE000 { return (0, 0, EILSEQ); }
5659            if c >= 0x110000 { return (0, 0, EILSEQ); }
5660            (c, 4, 0)
5661        }
5662        ENC_WCHAR_T => {
5663            if src_left < 4 { return (0, 0, EINVAL); }
5664            let c = *(src as *const u32);
5665            if c >= 0xD800 && c < 0xE000 { return (0, 0, EILSEQ); }
5666            if c >= 0x110000 { return (0, 0, EILSEQ); }
5667            (c, 4, 0)
5668        }
5669        ENC_ASCII => {
5670            let c = *src as u32;
5671            if c >= 128 { return (0, 0, EILSEQ); }
5672            (c, 1, 0)
5673        }
5674        ENC_LATIN1 => (*src as u32, 1, 0),
5675        ENC_ISO8859_2 => (iso8859_to_u(&ISO8859_2_TO_U, *src), 1, 0),
5676        ENC_ISO8859_3 => (iso8859_to_u(&ISO8859_3_TO_U, *src), 1, 0),
5677        ENC_ISO8859_4 => (iso8859_to_u(&ISO8859_4_TO_U, *src), 1, 0),
5678        ENC_ISO8859_5 => (iso8859_to_u(&ISO8859_5_TO_U, *src), 1, 0),
5679        ENC_ISO8859_6 => (iso8859_to_u(&ISO8859_6_TO_U, *src), 1, 0),
5680        ENC_ISO8859_7 => (iso8859_to_u(&ISO8859_7_TO_U, *src), 1, 0),
5681        ENC_ISO8859_8 => (iso8859_to_u(&ISO8859_8_TO_U, *src), 1, 0),
5682        ENC_ISO8859_9 => (iso8859_to_u(&ISO8859_9_TO_U, *src), 1, 0),
5683        ENC_ISO8859_10 => (iso8859_to_u(&ISO8859_10_TO_U, *src), 1, 0),
5684        ENC_ISO8859_11 => (iso8859_to_u(&ISO8859_11_TO_U, *src), 1, 0),
5685        ENC_ISO8859_13 => (iso8859_to_u(&ISO8859_13_TO_U, *src), 1, 0),
5686        ENC_ISO8859_14 => (iso8859_to_u(&ISO8859_14_TO_U, *src), 1, 0),
5687        ENC_ISO8859_15 => (iso8859_to_u(&ISO8859_15_TO_U, *src), 1, 0),
5688        ENC_ISO8859_16 => (iso8859_to_u(&ISO8859_16_TO_U, *src), 1, 0),
5689        ENC_WIN1252 => {
5690            let b = *src;
5691            if b < 128 { return (b as u32, 1, 0); }
5692            (WIN1252_TO_U[b as usize - 128], 1, 0)
5693        }
5694        ENC_WIN1251 => {
5695            let b = *src;
5696            if b < 128 { return (b as u32, 1, 0); }
5697            (WIN1251_TO_U[b as usize - 128], 1, 0)
5698        }
5699        ENC_KOI8R => {
5700            let b = *src;
5701            if b < 128 { return (b as u32, 1, 0); }
5702            (KOI8R_TO_U[b as usize - 128], 1, 0)
5703        }
5704        ENC_GBK | ENC_GB2312 => {
5705            let c = *src;
5706            if c < 128 { return (c as u32, 1, 0); }
5707            if c == 128 { return (0x20AC, 1, 0); }
5708            if src_left < 2 { return (0, 0, EINVAL); }
5709            let d = *src.add(1);
5710            if d < 0x40 || d == 0x7F || d > 0xFE { return (0, 0, EILSEQ); }
5711            let row = (c as usize).wrapping_sub(0x81);
5712            if row >= 126 { return (0, 0, EILSEQ); }
5713            let mut col = (d as usize) - 0x40;
5714            if col > 63 { col -= 1; }
5715            let cp = GB18030_TABLE[row * 190 + col] as u32;
5716            if cp == 0 { return (0, 0, EILSEQ); }
5717            (cp, 2, 0)
5718        }
5719        ENC_BIG5 => {
5720            let c = *src;
5721            if c < 128 { return (c as u32, 1, 0); }
5722            if src_left < 2 { return (0, 0, EINVAL); }
5723            let d = *src.add(1);
5724            if d < 0x40 || d == 0x7F || d > 0xFE { return (0, 0, EILSEQ); }
5725            let mut col = (d as usize) - 0x40;
5726            if col > 0x3E { col -= 0x22; }
5727            if c >= 0xA1 && c < 0xFA {
5728                let row = (c as usize) - 0xA1;
5729                if row >= 89 { return (0, 0, EILSEQ); }
5730                let cp = BIG5_TABLE[row * 157 + col] as u32;
5731                if cp == 0 { return (0, 0, EILSEQ); }
5732                return (cp, 2, 0);
5733            }
5734            (0, 0, EILSEQ)
5735        }
5736        ENC_EUCJP => {
5737            let c = *src;
5738            if c < 128 { return (c as u32, 1, 0); }
5739            if c == 0x8E {
5740                if src_left < 2 { return (0, 0, EINVAL); }
5741                let d = *src.add(1);
5742                if d < 0xA1 || d > 0xDF { return (0, 0, EILSEQ); }
5743                return ((d as u32) + 0xFF61 - 0xA1, 2, 0);
5744            }
5745            if c < 0xA1 { return (0, 0, EILSEQ); }
5746            if src_left < 2 { return (0, 0, EINVAL); }
5747            let d = *src.add(1);
5748            if d < 0xA1 || d > 0xFE { return (0, 0, EILSEQ); }
5749            let row = (c as usize) - 0xA1;
5750            let col = (d as usize) - 0xA1;
5751            if row >= 84 || col >= 94 { return (0, 0, EILSEQ); }
5752            let cp = JIS0208[row * 94 + col] as u32;
5753            if cp == 0 { return (0, 0, EILSEQ); }
5754            (cp, 2, 0)
5755        }
5756        ENC_SHIFTJIS => {
5757            let c = *src;
5758            if c < 128 { return (c as u32, 1, 0); }
5759            if c >= 0xA1 && c <= 0xDF { return ((c as u32) + 0xFF61 - 0xA1, 1, 0); }
5760            if src_left < 2 { return (0, 0, EINVAL); }
5761            let d = *src.add(1);
5762            if d < 0x40 || d == 0x7F || d > 0xFC { return (0, 0, EILSEQ); }
5763            let row = if c >= 129 && c <= 159 { (c as usize) - 129 }
5764                      else if c >= 224 && c <= 239 { (c as usize) - 193 }
5765                      else { return (0, 0, EILSEQ); };
5766            let (col, row_adj) = if d >= 64 && d <= 158 && d != 127 {
5767                let mut dd = d as usize;
5768                if dd > 127 { dd -= 1; }
5769                (dd - 64, row * 2)
5770            } else if d >= 159 && d <= 252 {
5771                ((d as usize) - 159, row * 2 + 1)
5772            } else {
5773                return (0, 0, EILSEQ);
5774            };
5775            if row_adj >= 84 { return (0, 0, EILSEQ); }
5776            let cp = JIS0208[row_adj * 94 + col] as u32;
5777            if cp == 0 { return (0, 0, EILSEQ); }
5778            (cp, 2, 0)
5779        }
5780        _ => (0, 0, EILSEQ),
5781    }
5782}
5783
5784unsafe fn iconv_encode(enc: i32, c: u32, dst: *mut u8, dst_left: usize) -> (usize, c_int) {
5785    match enc {
5786        ENC_UTF8 => {
5787            if c < 0x80 {
5788                if dst_left < 1 { return (0, E2BIG); }
5789                *dst = c as u8; return (1, 0);
5790            }
5791            if c < 0x800 {
5792                if dst_left < 2 { return (0, E2BIG); }
5793                *dst = (0xC0 | (c >> 6)) as u8;
5794                *dst.add(1) = (0x80 | (c & 0x3F)) as u8;
5795                return (2, 0);
5796            }
5797            if c < 0x10000 {
5798                if dst_left < 3 { return (0, E2BIG); }
5799                *dst = (0xE0 | (c >> 12)) as u8;
5800                *dst.add(1) = (0x80 | ((c >> 6) & 0x3F)) as u8;
5801                *dst.add(2) = (0x80 | (c & 0x3F)) as u8;
5802                return (3, 0);
5803            }
5804            if dst_left < 4 { return (0, E2BIG); }
5805            *dst = (0xF0 | (c >> 18)) as u8;
5806            *dst.add(1) = (0x80 | ((c >> 12) & 0x3F)) as u8;
5807            *dst.add(2) = (0x80 | ((c >> 6) & 0x3F)) as u8;
5808            *dst.add(3) = (0x80 | (c & 0x3F)) as u8;
5809            (4, 0)
5810        }
5811        ENC_UTF16LE => {
5812            if c < 0x10000 {
5813                if dst_left < 2 { return (0, E2BIG); }
5814                *dst = (c & 0xFF) as u8;
5815                *dst.add(1) = (c >> 8) as u8;
5816                return (2, 0);
5817            }
5818            if dst_left < 4 { return (0, E2BIG); }
5819            let c = c - 0x10000;
5820            let hi = ((c >> 10) + 0xD800) as u16;
5821            let lo = ((c & 0x3FF) + 0xDC00) as u16;
5822            *dst = (hi & 0xFF) as u8; *dst.add(1) = (hi >> 8) as u8;
5823            *dst.add(2) = (lo & 0xFF) as u8; *dst.add(3) = (lo >> 8) as u8;
5824            (4, 0)
5825        }
5826        ENC_UTF16BE => {
5827            if c < 0x10000 {
5828                if dst_left < 2 { return (0, E2BIG); }
5829                *dst = (c >> 8) as u8;
5830                *dst.add(1) = (c & 0xFF) as u8;
5831                return (2, 0);
5832            }
5833            if dst_left < 4 { return (0, E2BIG); }
5834            let c = c - 0x10000;
5835            let hi = ((c >> 10) + 0xD800) as u16;
5836            let lo = ((c & 0x3FF) + 0xDC00) as u16;
5837            *dst = (hi >> 8) as u8; *dst.add(1) = (hi & 0xFF) as u8;
5838            *dst.add(2) = (lo >> 8) as u8; *dst.add(3) = (lo & 0xFF) as u8;
5839            (4, 0)
5840        }
5841        ENC_UTF32LE => {
5842            if dst_left < 4 { return (0, E2BIG); }
5843            *dst = (c & 0xFF) as u8; *dst.add(1) = ((c >> 8) & 0xFF) as u8;
5844            *dst.add(2) = ((c >> 16) & 0xFF) as u8; *dst.add(3) = (c >> 24) as u8;
5845            (4, 0)
5846        }
5847        ENC_UTF32BE => {
5848            if dst_left < 4 { return (0, E2BIG); }
5849            *dst = (c >> 24) as u8; *dst.add(1) = ((c >> 16) & 0xFF) as u8;
5850            *dst.add(2) = ((c >> 8) & 0xFF) as u8; *dst.add(3) = (c & 0xFF) as u8;
5851            (4, 0)
5852        }
5853        ENC_WCHAR_T => {
5854            if dst_left < 4 { return (0, E2BIG); }
5855            *(dst as *mut u32) = c;
5856            (4, 0)
5857        }
5858        ENC_ASCII => {
5859            if c < 128 {
5860                if dst_left < 1 { return (0, E2BIG); }
5861                *dst = c as u8; return (1, 0);
5862            }
5863            (0, EILSEQ)
5864        }
5865        ENC_LATIN1 => {
5866            if c < 256 {
5867                if dst_left < 1 { return (0, E2BIG); }
5868                *dst = c as u8; return (1, 0);
5869            }
5870            (0, EILSEQ)
5871        }
5872        ENC_ISO8859_2 => {
5873            if let Some(b) = u_to_iso8859(&ISO8859_2_TO_U, c) {
5874                if dst_left < 1 { return (0, E2BIG); }
5875                *dst = b; return (1, 0);
5876            }
5877            (0, EILSEQ)
5878        }
5879        ENC_ISO8859_3 => {
5880            if let Some(b) = u_to_iso8859(&ISO8859_3_TO_U, c) {
5881                if dst_left < 1 { return (0, E2BIG); }
5882                *dst = b; return (1, 0);
5883            }
5884            (0, EILSEQ)
5885        }
5886        ENC_ISO8859_4 => {
5887            if let Some(b) = u_to_iso8859(&ISO8859_4_TO_U, c) {
5888                if dst_left < 1 { return (0, E2BIG); }
5889                *dst = b; return (1, 0);
5890            }
5891            (0, EILSEQ)
5892        }
5893        ENC_ISO8859_5 => {
5894            if let Some(b) = u_to_iso8859(&ISO8859_5_TO_U, c) {
5895                if dst_left < 1 { return (0, E2BIG); }
5896                *dst = b; return (1, 0);
5897            }
5898            (0, EILSEQ)
5899        }
5900        ENC_ISO8859_6 => {
5901            if let Some(b) = u_to_iso8859(&ISO8859_6_TO_U, c) {
5902                if dst_left < 1 { return (0, E2BIG); }
5903                *dst = b; return (1, 0);
5904            }
5905            (0, EILSEQ)
5906        }
5907        ENC_ISO8859_7 => {
5908            if let Some(b) = u_to_iso8859(&ISO8859_7_TO_U, c) {
5909                if dst_left < 1 { return (0, E2BIG); }
5910                *dst = b; return (1, 0);
5911            }
5912            (0, EILSEQ)
5913        }
5914        ENC_ISO8859_8 => {
5915            if let Some(b) = u_to_iso8859(&ISO8859_8_TO_U, c) {
5916                if dst_left < 1 { return (0, E2BIG); }
5917                *dst = b; return (1, 0);
5918            }
5919            (0, EILSEQ)
5920        }
5921        ENC_ISO8859_9 => {
5922            if let Some(b) = u_to_iso8859(&ISO8859_9_TO_U, c) {
5923                if dst_left < 1 { return (0, E2BIG); }
5924                *dst = b; return (1, 0);
5925            }
5926            (0, EILSEQ)
5927        }
5928        ENC_ISO8859_10 => {
5929            if let Some(b) = u_to_iso8859(&ISO8859_10_TO_U, c) {
5930                if dst_left < 1 { return (0, E2BIG); }
5931                *dst = b; return (1, 0);
5932            }
5933            (0, EILSEQ)
5934        }
5935        ENC_ISO8859_11 => {
5936            if let Some(b) = u_to_iso8859(&ISO8859_11_TO_U, c) {
5937                if dst_left < 1 { return (0, E2BIG); }
5938                *dst = b; return (1, 0);
5939            }
5940            (0, EILSEQ)
5941        }
5942        ENC_ISO8859_13 => {
5943            if let Some(b) = u_to_iso8859(&ISO8859_13_TO_U, c) {
5944                if dst_left < 1 { return (0, E2BIG); }
5945                *dst = b; return (1, 0);
5946            }
5947            (0, EILSEQ)
5948        }
5949        ENC_ISO8859_14 => {
5950            if let Some(b) = u_to_iso8859(&ISO8859_14_TO_U, c) {
5951                if dst_left < 1 { return (0, E2BIG); }
5952                *dst = b; return (1, 0);
5953            }
5954            (0, EILSEQ)
5955        }
5956        ENC_ISO8859_15 => {
5957            if let Some(b) = u_to_iso8859(&ISO8859_15_TO_U, c) {
5958                if dst_left < 1 { return (0, E2BIG); }
5959                *dst = b; return (1, 0);
5960            }
5961            (0, EILSEQ)
5962        }
5963        ENC_ISO8859_16 => {
5964            if let Some(b) = u_to_iso8859(&ISO8859_16_TO_U, c) {
5965                if dst_left < 1 { return (0, E2BIG); }
5966                *dst = b; return (1, 0);
5967            }
5968            (0, EILSEQ)
5969        }
5970        ENC_WIN1252 => {
5971            if c < 128 {
5972                if dst_left < 1 { return (0, E2BIG); }
5973                *dst = c as u8; return (1, 0);
5974            }
5975            if c >= 0xA0 && c <= 0xFF && WIN1252_TO_U[c as usize - 128] == c {
5976                if dst_left < 1 { return (0, E2BIG); }
5977                *dst = c as u8; return (1, 0);
5978            }
5979            for i in 0..128usize {
5980                if WIN1252_TO_U[i] == c {
5981                    if dst_left < 1 { return (0, E2BIG); }
5982                    *dst = (i + 128) as u8; return (1, 0);
5983                }
5984            }
5985            (0, EILSEQ)
5986        }
5987        ENC_WIN1251 => {
5988            if c < 128 {
5989                if dst_left < 1 { return (0, E2BIG); }
5990                *dst = c as u8; return (1, 0);
5991            }
5992            for i in 0..128usize {
5993                if WIN1251_TO_U[i] == c {
5994                    if dst_left < 1 { return (0, E2BIG); }
5995                    *dst = (i + 128) as u8; return (1, 0);
5996                }
5997            }
5998            (0, EILSEQ)
5999        }
6000        ENC_KOI8R => {
6001            if c < 128 {
6002                if dst_left < 1 { return (0, E2BIG); }
6003                *dst = c as u8; return (1, 0);
6004            }
6005            for i in 0..128usize {
6006                if KOI8R_TO_U[i] == c {
6007                    if dst_left < 1 { return (0, E2BIG); }
6008                    *dst = (i + 128) as u8; return (1, 0);
6009                }
6010            }
6011            (0, EILSEQ)
6012        }
6013        ENC_GBK | ENC_GB2312 => {
6014            if c < 128 {
6015                if dst_left < 1 { return (0, E2BIG); }
6016                *dst = c as u8; return (1, 0);
6017            }
6018            if c == 0x20AC {
6019                if dst_left < 1 { return (0, E2BIG); }
6020                *dst = 128; return (1, 0);
6021            }
6022            for row in 0..126usize {
6023                for col in 0..190usize {
6024                    if GB18030_TABLE[row * 190 + col] as u32 == c {
6025                        if dst_left < 2 { return (0, E2BIG); }
6026                        *dst = (row + 0x81) as u8;
6027                        *dst.add(1) = if col <= 62 { (col + 0x40) as u8 } else { (col + 0x41) as u8 };
6028                        return (2, 0);
6029                    }
6030                }
6031            }
6032            (0, EILSEQ)
6033        }
6034        ENC_BIG5 => {
6035            if c < 128 {
6036                if dst_left < 1 { return (0, E2BIG); }
6037                *dst = c as u8; return (1, 0);
6038            }
6039            for row in 0..89usize {
6040                for col in 0..157usize {
6041                    if BIG5_TABLE[row * 157 + col] as u32 == c {
6042                        if dst_left < 2 { return (0, E2BIG); }
6043                        *dst = (row + 0xA1) as u8;
6044                        *dst.add(1) = if col <= 0x3E { (col + 0x40) as u8 } else { (col + 0x62) as u8 };
6045                        return (2, 0);
6046                    }
6047                }
6048            }
6049            (0, EILSEQ)
6050        }
6051        ENC_EUCJP => {
6052            if c < 128 {
6053                if dst_left < 1 { return (0, E2BIG); }
6054                *dst = c as u8; return (1, 0);
6055            }
6056            if c >= 0xFF61 && c <= 0xFF9F {
6057                if dst_left < 2 { return (0, E2BIG); }
6058                *dst = 0x8E;
6059                *dst.add(1) = (c - 0xFF61 + 0xA1) as u8;
6060                return (2, 0);
6061            }
6062            let jis = uni_to_jis(c);
6063            if jis == 0 { return (0, EILSEQ); }
6064            if dst_left < 2 { return (0, E2BIG); }
6065            *dst = ((jis >> 8) as u8).wrapping_add(0x80);
6066            *dst.add(1) = ((jis & 0xFF) as u8).wrapping_add(0x80);
6067            (2, 0)
6068        }
6069        ENC_SHIFTJIS => {
6070            if c < 128 {
6071                if dst_left < 1 { return (0, E2BIG); }
6072                *dst = c as u8; return (1, 0);
6073            }
6074            if c >= 0xFF61 && c <= 0xFF9F {
6075                if dst_left < 1 { return (0, E2BIG); }
6076                *dst = (c - 0xFF61 + 0xA1) as u8;
6077                return (1, 0);
6078            }
6079            let jis = uni_to_jis(c);
6080            if jis == 0 { return (0, EILSEQ); }
6081            if dst_left < 2 { return (0, E2BIG); }
6082            let r = (jis >> 8) as usize;
6083            let d = (jis & 0xFF) as usize;
6084            *dst = ((r + 1) / 2 + if r < 95 { 112 } else { 176 }) as u8;
6085            *dst.add(1) = if r % 2 == 1 { (d + 31 + d / 96) as u8 } else { (d + 126) as u8 };
6086            (2, 0)
6087        }
6088        _ => (0, EILSEQ),
6089    }
6090}
6091
6092#[no_mangle]
6093pub unsafe extern "C" fn iconv_open(tocode: *const c_char, fromcode: *const c_char) -> IconvT {
6094    let to = find_encoding(tocode as *const u8);
6095    let from = find_encoding(fromcode as *const u8);
6096    if to < 0 || from < 0 {
6097        ERRNO!() = EINVAL;
6098        return !0usize as IconvT;
6099    }
6100    make_cd(from, to)
6101}
6102
6103#[no_mangle]
6104pub unsafe extern "C" fn iconv(
6105    cd: IconvT,
6106    inbuf: *mut *mut c_char,
6107    inbytesleft: *mut SizeT,
6108    outbuf: *mut *mut c_char,
6109    outbytesleft: *mut SizeT,
6110) -> SizeT {
6111    if cd == !0usize as IconvT {
6112        ERRNO!() = EINVAL;
6113        return !0usize;
6114    }
6115    if inbuf.is_null() || outbuf.is_null() { return 0; }
6116    let from = extract_from(cd);
6117    let to = extract_to(cd);
6118    let mut src = *inbuf as *const u8;
6119    let mut dst = *outbuf as *mut u8;
6120    let mut src_left = *inbytesleft;
6121    let mut dst_left = *outbytesleft;
6122    let mut subst = 0usize;
6123    while src_left > 0 {
6124        let (cp, consumed, err) = iconv_decode(from, src, src_left);
6125        if err != 0 {
6126            if err == EINVAL { break; }
6127            ERRNO!() = err;
6128            return !0usize;
6129        }
6130        let (written, err) = iconv_encode(to, cp, dst, dst_left);
6131        if err != 0 {
6132            if err == E2BIG {
6133                ERRNO!() = E2BIG;
6134                return !0usize;
6135            }
6136            if dst_left < 1 {
6137                ERRNO!() = E2BIG;
6138                return !0usize;
6139            }
6140            *dst = b'*';
6141            dst = dst.add(1);
6142            dst_left -= 1;
6143            subst += 1;
6144        } else {
6145            dst = dst.add(written);
6146            dst_left -= written;
6147        }
6148        src = src.add(consumed);
6149        src_left -= consumed;
6150    }
6151    *inbuf = src as *mut c_char;
6152    *outbuf = dst as *mut c_char;
6153    *inbytesleft = src_left;
6154    *outbytesleft = dst_left;
6155    subst
6156}
6157
6158#[no_mangle]
6159pub unsafe extern "C" fn iconv_close(_cd: IconvT) -> c_int { 0 }
6160
6161// ============================================================
6162// Syscall wrappers as public C ABI
6163// ============================================================
6164
6165#[no_mangle]
6166pub unsafe extern "C" fn write(fd: c_int, buf: *const c_void, count: SizeT) -> SSizeT {
6167    syscall_ret(sys_write(fd as i64, buf as *const u8, count)) as SSizeT
6168}
6169
6170#[no_mangle]
6171pub unsafe extern "C" fn read(fd: c_int, buf: *mut c_void, count: SizeT) -> SSizeT {
6172    syscall_ret(sys_read(fd as i64, buf as *mut u8, count)) as SSizeT
6173}
6174
6175#[no_mangle]
6176pub unsafe extern "C" fn open(path: *const c_char, flags: c_int, mode: c_int) -> c_int {
6177    syscall_ret(sys_open(path as *const u8, flags as i64, mode as i64)) as c_int
6178}
6179
6180#[no_mangle]
6181pub unsafe extern "C" fn close(fd: c_int) -> c_int {
6182    syscall_ret(sys_close(fd as i64)) as c_int
6183}
6184
6185#[no_mangle]
6186pub unsafe extern "C" fn lseek(fd: c_int, offset: i64, whence: c_int) -> i64 {
6187    syscall_ret(sys_lseek(fd as i64, offset, whence as i64))
6188}
6189
6190// ============================================================
6191// Additional syscall wrappers for stdio
6192// ============================================================
6193
6194const O_RDONLY: i32 = 0;
6195const O_WRONLY: i32 = 1;
6196const O_RDWR: i32 = 2;
6197const O_CREAT: i32 = 64;
6198const O_TRUNC: i32 = 512;
6199const O_APPEND: i32 = 1024;
6200const O_EXCL: i32 = 128;
6201const O_NONBLOCK: i32 = 2048;
6202const O_NOFOLLOW: i32 = if cfg!(target_arch = "aarch64") { 0o100000 } else { 0o400000 };
6203const O_DIRECTORY: i32 = if cfg!(target_arch = "aarch64") { 0o40000 } else { 0o200000 };
6204const O_CLOEXEC: i32 = 0x80000;
6205
6206const F_GETFD: i32 = 1;
6207const F_SETFD: i32 = 2;
6208const F_GETFL: i32 = 3;
6209const F_SETFL: i32 = 4;
6210const F_GETLK: i32 = 5;
6211const F_SETLK: i32 = 6;
6212const F_SETLKW: i32 = 7;
6213const F_DUPFD: i32 = 0;
6214const F_DUPFD_CLOEXEC: i32 = 1030;
6215const FD_CLOEXEC: i32 = 1;
6216
6217const TIOCGWINSZ: u32 = 0x5413;
6218
6219#[repr(C)]
6220struct winsize {
6221    ws_row: u16,
6222    ws_col: u16,
6223    ws_xpixel: u16,
6224    ws_ypixel: u16,
6225}
6226
6227#[inline]
6228unsafe fn sys_pipe2(fds: *mut c_int, flags: i32) -> i64 {
6229    <Arch as Syscalls>::syscall2(SYS_PIPE2, fds as i64, flags as i64)
6230}
6231
6232#[inline]
6233unsafe fn sys_dup3(oldfd: i32, newfd: i32, flags: i32) -> i64 {
6234    <Arch as Syscalls>::syscall3(SYS_DUP3, oldfd as i64, newfd as i64, flags as i64)
6235}
6236
6237#[inline]
6238unsafe fn sys_fcntl(fd: i32, cmd: i32, arg: i64) -> i64 {
6239    <Arch as Syscalls>::syscall3(SYS_FCNTL, fd as i64, cmd as i64, arg as i64)
6240}
6241
6242#[inline]
6243unsafe fn sys_ioctl(fd: c_int, request: u32, arg: *mut u8) -> i64 {
6244    <Arch as Syscalls>::syscall3(SYS_IOCTL, fd as i64, request as i64, arg as i64)
6245}
6246
6247#[inline]
6248unsafe fn sys_unlinkat(dirfd: i32, path: *const u8, flags: i32) -> i64 {
6249    <Arch as Syscalls>::syscall3(SYS_UNLINKAT, dirfd as i64, path as i64, flags as i64)
6250}
6251
6252#[inline]
6253unsafe fn sys_renameat2(olddirfd: i32, oldpath: *const u8, newdirfd: i32, newpath: *const u8, flags: u32) -> i64 {
6254    <Arch as Syscalls>::syscall5(SYS_RENAMEAT2, olddirfd as i64, oldpath as i64, newdirfd as i64, newpath as i64, flags as i64)
6255}
6256
6257// ============================================================
6258// FILE / stdio (buffered, musl-compatible layout)
6259// ============================================================
6260
6261const UNGET: usize = 8;
6262const F_PERM: u32 = 1;
6263const F_NORD: u32 = 4;
6264const F_NOWR: u32 = 8;
6265const F_EOF: u32 = 16;
6266const F_ERR: u32 = 32;
6267const F_SVB: u32 = 64;
6268const F_APP: u32 = 128;
6269
6270const BUFSIZ: usize = 1024;
6271
6272const _IOFBF: c_int = 0;
6273const _IOLBF: c_int = 1;
6274const _IONBF: c_int = 2;
6275
6276const SEEK_SET: c_int = 0;
6277const SEEK_CUR: c_int = 1;
6278const SEEK_END: c_int = 2;
6279
6280// ponytail: buffered FILE, musl x86_64 layout subset
6281#[repr(C)]
6282pub struct FILE {
6283    flags: c_uint,
6284    rpos: *mut u8,
6285    rend: *mut u8,
6286    close: Option<unsafe extern "C" fn(*mut FILE) -> c_int>,
6287    wend: *mut u8,
6288    wpos: *mut u8,
6289    mustbezero_1: *mut u8,
6290    wbase: *mut u8,
6291    read_fn: Option<unsafe extern "C" fn(*mut FILE, *mut u8, usize) -> usize>,
6292    write_fn: Option<unsafe extern "C" fn(*mut FILE, *const u8, usize) -> usize>,
6293    seek_fn: Option<unsafe extern "C" fn(*mut FILE, i64, c_int) -> i64>,
6294    buf: *mut u8,
6295    buf_size: usize,
6296    prev: *mut FILE,
6297    next: *mut FILE,
6298    fd: c_int,
6299    pipe_pid: c_int,
6300    lockcount: c_long,
6301    mode: c_int,
6302    lock: c_int,
6303    lbf: c_int,
6304    cookie: *mut c_void,
6305    off: i64,
6306    getln_buf: *mut c_char,
6307    mustbezero_2: *mut c_void,
6308    shend: *mut u8,
6309    shlim: i64,
6310    shcnt: i64,
6311    ungotten: [c_int; UNGET],
6312    ungotten_count: c_int,
6313    _eof: c_int,
6314    _err: c_int,
6315}
6316
6317static mut STDIN_BUF: [u8; BUFSIZ] = [0; BUFSIZ];
6318static mut STDOUT_BUF: [u8; BUFSIZ] = [0; BUFSIZ];
6319static mut STDERR_BUF: [u8; BUFSIZ] = [0; BUFSIZ];
6320
6321static mut STDIN_FILE: FILE = FILE {
6322    flags: 0, rpos: core::ptr::null_mut(), rend: core::ptr::null_mut(),
6323    close: None, wend: core::ptr::null_mut(), wpos: core::ptr::null_mut(),
6324    mustbezero_1: core::ptr::null_mut(), wbase: core::ptr::null_mut(),
6325    read_fn: None, write_fn: None, seek_fn: None,
6326    buf: core::ptr::null_mut(), buf_size: BUFSIZ,
6327    prev: core::ptr::null_mut(), next: core::ptr::null_mut(),
6328    fd: 0, pipe_pid: 0, lockcount: 0, mode: 0, lock: 0, lbf: -1,
6329    cookie: core::ptr::null_mut(), off: 0,
6330    getln_buf: core::ptr::null_mut(), mustbezero_2: core::ptr::null_mut(),
6331    shend: core::ptr::null_mut(), shlim: 0, shcnt: 0,
6332    ungotten: [0; UNGET], ungotten_count: 0, _eof: 0, _err: 0,
6333};
6334static mut STDOUT_FILE: FILE = FILE {
6335    flags: F_NOWR as u32 + F_SVB as u32, rpos: core::ptr::null_mut(), rend: core::ptr::null_mut(),
6336    close: None, wend: core::ptr::null_mut(), wpos: core::ptr::null_mut(),
6337    mustbezero_1: core::ptr::null_mut(), wbase: core::ptr::null_mut(),
6338    read_fn: None, write_fn: None, seek_fn: None,
6339    buf: core::ptr::null_mut(), buf_size: BUFSIZ,
6340    prev: core::ptr::null_mut(), next: core::ptr::null_mut(),
6341    fd: 1, pipe_pid: 0, lockcount: 0, mode: 0, lock: 0, lbf: -1,
6342    cookie: core::ptr::null_mut(), off: 0,
6343    getln_buf: core::ptr::null_mut(), mustbezero_2: core::ptr::null_mut(),
6344    shend: core::ptr::null_mut(), shlim: 0, shcnt: 0,
6345    ungotten: [0; UNGET], ungotten_count: 0, _eof: 0, _err: 0,
6346};
6347static mut STDERR_FILE: FILE = FILE {
6348    flags: F_NOWR as u32, rpos: core::ptr::null_mut(), rend: core::ptr::null_mut(),
6349    close: None, wend: core::ptr::null_mut(), wpos: core::ptr::null_mut(),
6350    mustbezero_1: core::ptr::null_mut(), wbase: core::ptr::null_mut(),
6351    read_fn: None, write_fn: None, seek_fn: None,
6352    buf: core::ptr::null_mut(), buf_size: BUFSIZ,
6353    prev: core::ptr::null_mut(), next: core::ptr::null_mut(),
6354    fd: 2, pipe_pid: 0, lockcount: 0, mode: 0, lock: 0, lbf: -1,
6355    cookie: core::ptr::null_mut(), off: 0,
6356    getln_buf: core::ptr::null_mut(), mustbezero_2: core::ptr::null_mut(),
6357    shend: core::ptr::null_mut(), shlim: 0, shcnt: 0,
6358    ungotten: [0; UNGET], ungotten_count: 0, _eof: 0, _err: 0,
6359};
6360
6361#[no_mangle]
6362pub static mut stdin: *mut FILE = &raw mut STDIN_FILE as *mut FILE;
6363#[no_mangle]
6364pub static mut stdout: *mut FILE = &raw mut STDOUT_FILE as *mut FILE;
6365#[no_mangle]
6366pub static mut stderr: *mut FILE = &raw mut STDERR_FILE as *mut FILE;
6367
6368// ============================================================
6369// FILE helpers
6370// ============================================================
6371
6372unsafe fn __stdio_init() {
6373    STDIN_FILE.buf = core::ptr::addr_of_mut!(STDIN_BUF) as *mut u8;
6374    STDIN_FILE.buf_size = BUFSIZ;
6375    STDIN_FILE.read_fn = Some(__stdio_read);
6376    STDIN_FILE.write_fn = Some(__stdio_write);
6377    STDIN_FILE.seek_fn = Some(__stdio_seek);
6378    STDOUT_FILE.buf = core::ptr::addr_of_mut!(STDOUT_BUF) as *mut u8;
6379    STDOUT_FILE.buf_size = BUFSIZ;
6380    STDOUT_FILE.lbf = b'\n' as c_int;
6381    STDOUT_FILE.read_fn = Some(__stdio_read);
6382    STDOUT_FILE.write_fn = Some(__stdio_write);
6383    STDOUT_FILE.seek_fn = Some(__stdio_seek);
6384    STDERR_FILE.buf = core::ptr::addr_of_mut!(STDERR_BUF) as *mut u8;
6385    STDERR_FILE.buf_size = BUFSIZ;
6386    STDERR_FILE.read_fn = Some(__stdio_read);
6387    STDERR_FILE.write_fn = Some(__stdio_write);
6388    STDERR_FILE.seek_fn = Some(__stdio_seek);
6389}
6390
6391unsafe fn buf_ptr(f: *mut FILE) -> *mut u8 {
6392    (f as *mut u8).add(core::mem::size_of::<FILE>())
6393}
6394
6395unsafe extern "C" fn __stdio_read(f: *mut FILE, buf: *mut u8, len: usize) -> usize {
6396    let n = sys_read((*f).fd as i64, buf, len);
6397    if n <= 0 {
6398        if n == 0 { (*f).flags |= F_EOF; } else { (*f).flags |= F_ERR; (*f)._err = 1; }
6399        return 0;
6400    }
6401    n as usize
6402}
6403
6404unsafe extern "C" fn __stdio_write(f: *mut FILE, buf: *const u8, len: usize) -> usize {
6405    let l = (*f).wpos as usize - (*f).wbase as usize;
6406    let mut iov = [
6407        ((*f).wbase as *const u8, l),
6408        (buf, len),
6409    ];
6410    if iov[0].1 == 0 { iov = [iov[1], (core::ptr::null(), 0)]; }
6411    if (*f).flags & F_APP != 0 {
6412        let _ = sys_lseek((*f).fd as i64, 0, SEEK_END as i64);
6413    }
6414    let mut rem = iov[0].1 + iov[1].1;
6415    let mut idx = 0usize;
6416    while rem > 0 {
6417        let ptr = iov[idx].0;
6418        let cnt = iov[idx].1;
6419        if cnt == 0 { idx += 1; continue; }
6420        let n = sys_write((*f).fd as i64, ptr, cnt);
6421        if n <= 0 {
6422            (*f).wpos = core::ptr::null_mut();
6423            (*f).wbase = core::ptr::null_mut();
6424            (*f).wend = core::ptr::null_mut();
6425            (*f).flags |= F_ERR;
6426            return if iov[idx].0 == buf { 0 } else { len };
6427        }
6428        rem -= n as usize;
6429        iov[idx].0 = iov[idx].0.add(n as usize);
6430        iov[idx].1 -= n as usize;
6431        if iov[idx].1 == 0 { idx += 1; }
6432    }
6433    (*f).wend = (*f).buf.add((*f).buf_size);
6434    (*f).wbase = (*f).buf;
6435    (*f).wpos = (*f).buf;
6436    len
6437}
6438
6439unsafe extern "C" fn __stdio_seek(f: *mut FILE, off: i64, whence: c_int) -> i64 {
6440    sys_lseek((*f).fd as i64, off, whence as i64)
6441}
6442
6443unsafe fn init_file(
6444    f: *mut FILE,
6445    fd: c_int,
6446    mode: *const c_char,
6447    close_fn: Option<unsafe extern "C" fn(*mut FILE) -> c_int>,
6448    buf_area: *mut u8,
6449    buf_sz: usize,
6450) {
6451    core::ptr::write_bytes(f as *mut u8, 0, core::mem::size_of::<FILE>());
6452    (*f).fd = fd;
6453    (*f).close = close_fn;
6454    (*f).lock = 0;
6455    (*f).lbf = -1;
6456    (*f).buf = buf_area;
6457    (*f).buf_size = buf_sz;
6458    (*f).read_fn = Some(__stdio_read);
6459    (*f).write_fn = Some(__stdio_write);
6460    (*f).seek_fn = Some(__stdio_seek);
6461    let m = *mode;
6462    let has_plus = !strchr(mode as *const u8, b'+' as c_int).is_null();
6463    if m == b'r' as c_char && !has_plus {
6464        (*f).flags = F_NORD;
6465    } else if !has_plus {
6466        (*f).flags = F_NOWR;
6467    }
6468}
6469
6470unsafe extern "C" fn __stdio_close(f: *mut FILE) -> c_int {
6471    sys_close((*f).fd as i64);
6472    0
6473}
6474
6475unsafe fn fmodeflags(mode: *const c_char) -> c_int {
6476    let mut flags: c_int;
6477    let m = *mode;
6478    if !strchr(mode as *const u8, b'+' as c_int).is_null() {
6479        flags = O_RDWR;
6480    } else if m == b'r' as c_char {
6481        flags = O_RDONLY;
6482    } else {
6483        flags = O_WRONLY;
6484    }
6485    if !strchr(mode as *const u8, b'x' as c_int).is_null() { flags |= O_EXCL; }
6486    if !strchr(mode as *const u8, b'e' as c_int).is_null() { flags |= O_CLOEXEC; }
6487    if m != b'r' as c_char { flags |= O_CREAT; }
6488    if m == b'w' as c_char { flags |= O_TRUNC; }
6489    if m == b'a' as c_char { flags |= O_APPEND; }
6490    flags
6491}
6492
6493const O_ACCMODE: c_int = O_RDONLY | O_WRONLY | O_RDWR;
6494
6495unsafe fn flush_buf(f: *mut FILE) -> c_int {
6496    if (*f).wpos == (*f).wbase {
6497        if !(*f).wpos.is_null() {
6498            (*f).wend = (*f).buf.add((*f).buf_size);
6499            (*f).wpos = (*f).wbase;
6500        }
6501        return 0;
6502    }
6503    if let Some(wfunc) = (*f).write_fn {
6504        let ret = wfunc(f, core::ptr::null(), 0);
6505        if ret == 0 && (*f).wpos.is_null() { return -1; }
6506        return 0;
6507    }
6508    (*f).flags |= F_ERR;
6509    -1
6510}
6511
6512// ============================================================
6513// fopen / fdopen / freopen / fclose
6514// ============================================================
6515
6516#[no_mangle]
6517pub unsafe extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE {
6518    if filename.is_null() || mode.is_null() { ERRNO!() = EINVAL; return core::ptr::null_mut(); }
6519    let m = *mode;
6520    if m != b'r' as c_char && m != b'w' as c_char && m != b'a' as c_char {
6521        ERRNO!() = EINVAL; return core::ptr::null_mut();
6522    }
6523    let flags = fmodeflags(mode);
6524    let fd = sys_open(filename as *const u8, flags as i64, 0o666);
6525    if fd < 0 { ERRNO!() = (-fd) as c_int; return core::ptr::null_mut(); }
6526    fdopen(fd as c_int, mode)
6527}
6528
6529#[no_mangle]
6530pub unsafe extern "C" fn fdopen(fd: c_int, mode: *const c_char) -> *mut FILE {
6531    if fd < 0 || mode.is_null() { ERRNO!() = EINVAL; return core::ptr::null_mut(); }
6532    let m = *mode;
6533    if m != b'r' as c_char && m != b'w' as c_char && m != b'a' as c_char {
6534        ERRNO!() = EINVAL; return core::ptr::null_mut();
6535    }
6536    let f = calloc(1, core::mem::size_of::<FILE>() + UNGET + BUFSIZ) as *mut FILE;
6537    if f.is_null() { ERRNO!() = ENOMEM; return core::ptr::null_mut(); }
6538    let buf = buf_ptr(f);
6539    init_file(f, fd, mode, Some(__stdio_close), buf, BUFSIZ);
6540    if !strchr(mode as *const u8, b'a' as c_int).is_null() { (*f).flags |= F_APP; }
6541    if !strchr(mode as *const u8, b'e' as c_int).is_null() { sys_fcntl(fd, F_SETFD, FD_CLOEXEC as i64); }
6542    if (*f).flags & F_NOWR == 0 {
6543        let mut ws: winsize = core::mem::zeroed();
6544        if sys_ioctl(fd, TIOCGWINSZ, &mut ws as *mut winsize as *mut u8) == 0 {
6545            (*f).lbf = b'\n' as c_int;
6546        }
6547    }
6548    f
6549}
6550
6551#[no_mangle]
6552pub unsafe extern "C" fn freopen(
6553    filename: *const c_char, mode: *const c_char, f: *mut FILE,
6554) -> *mut FILE {
6555    fflush(f);
6556    if (*f).pipe_pid > 0 { let _ = __stdio_close(f); }
6557    if filename.is_null() {
6558        let flags = fmodeflags(mode);
6559        if flags == 0 { return core::ptr::null_mut(); }
6560        let mut fl = flags & !(O_CREAT | O_EXCL | O_CLOEXEC);
6561        if (*f).flags & F_APP != 0 { fl = (fl & !O_ACCMODE) | O_WRONLY | O_APPEND; }
6562        if sys_fcntl((*f).fd, F_SETFL, fl as i64) < 0 { return core::ptr::null_mut(); }
6563        let has_plus = !strchr(mode as *const u8, b'+' as c_int).is_null();
6564        if has_plus {
6565            (*f).flags &= !(F_NORD | F_NOWR);
6566        } else if *mode == b'r' as c_char {
6567            (*f).flags = ((*f).flags & !F_NOWR) | F_NORD;
6568        } else {
6569            (*f).flags = ((*f).flags & !F_NORD) | F_NOWR;
6570        }
6571        return f;
6572    }
6573    let new_f = fopen(filename, mode);
6574    if new_f.is_null() { let _ = __stdio_close(f); return core::ptr::null_mut(); }
6575    if (*new_f).fd == (*f).fd {
6576        (*new_f).fd = -1;
6577    } else {
6578        sys_dup3((*new_f).fd, (*f).fd, 0);
6579        let _ = __stdio_close(new_f);
6580    }
6581    (*f).flags = (*new_f).flags;
6582    (*f).rpos = core::ptr::null_mut();
6583    (*f).rend = core::ptr::null_mut();
6584    (*f).wpos = core::ptr::null_mut();
6585    (*f).wbase = core::ptr::null_mut();
6586    (*f).wend = core::ptr::null_mut();
6587    (*f).lbf = (*new_f).lbf;
6588    (*f).pipe_pid = 0;
6589    free(new_f as *mut c_void);
6590    f
6591}
6592
6593#[no_mangle]
6594pub unsafe extern "C" fn fclose(f: *mut FILE) -> c_int {
6595    if f.is_null() { return -1; }
6596    let r = fflush(f);
6597    if let Some(close_fn) = (*f).close { let _ = close_fn(f); }
6598    if r != 0 { -1 } else { 0 }
6599}
6600
6601// ============================================================
6602// setvbuf / setbuf / setbuffer / fflush
6603// ============================================================
6604
6605#[no_mangle]
6606pub unsafe extern "C" fn setvbuf(f: *mut FILE, buf: *mut c_char, mode: c_int, size: usize) -> c_int {
6607    if mode != _IOFBF && mode != _IOLBF && mode != _IONBF { return -1; }
6608    fflush(f);
6609    (*f).rpos = core::ptr::null_mut();
6610    (*f).rend = core::ptr::null_mut();
6611    (*f).wpos = core::ptr::null_mut();
6612    (*f).wbase = core::ptr::null_mut();
6613    (*f).wend = core::ptr::null_mut();
6614    if mode == _IONBF {
6615        (*f).buf_size = 0;
6616    } else if !buf.is_null() && size > 0 {
6617        (*f).buf = buf as *mut u8;
6618        (*f).buf_size = size;
6619    }
6620    (*f).lbf = if mode == _IOLBF { b'\n' as c_int } else { -1 };
6621    0
6622}
6623
6624#[no_mangle]
6625pub unsafe extern "C" fn setbuf(f: *mut FILE, buf: *mut c_char) {
6626    if buf.is_null() { setvbuf(f, core::ptr::null_mut(), _IONBF, 0); }
6627    else { setvbuf(f, buf, _IOFBF, BUFSIZ); }
6628}
6629
6630#[no_mangle]
6631pub unsafe extern "C" fn setbuffer(f: *mut FILE, buf: *mut c_char, size: usize) {
6632    if buf.is_null() { setvbuf(f, core::ptr::null_mut(), _IONBF, 0); }
6633    else { setvbuf(f, buf, _IOFBF, size); }
6634}
6635
6636#[no_mangle]
6637pub unsafe extern "C" fn fflush(f: *mut FILE) -> c_int {
6638    if f.is_null() {
6639        let mut r = 0;
6640        if !stdout.is_null() && !(*stdout).wpos.is_null() && (*stdout).wpos != (*stdout).wbase {
6641            if flush_buf(stdout) != 0 { r = -1; }
6642        }
6643        if !stderr.is_null() && !(*stderr).wpos.is_null() && (*stderr).wpos != (*stderr).wbase {
6644            if flush_buf(stderr) != 0 { r = -1; }
6645        }
6646        return r;
6647    }
6648    if !(*f).wpos.is_null() && (*f).wpos != (*f).wbase {
6649        return flush_buf(f);
6650    }
6651    0
6652}
6653
6654// ============================================================
6655// fwide / feof / ferror / clearerr / fileno
6656// ============================================================
6657
6658#[no_mangle]
6659pub unsafe extern "C" fn fwide(_f: *mut FILE, mode: c_int) -> c_int { mode }
6660
6661#[no_mangle]
6662pub unsafe extern "C" fn feof(f: *mut FILE) -> c_int { if (*f)._eof != 0 { 1 } else { 0 } }
6663
6664#[no_mangle]
6665pub unsafe extern "C" fn ferror(f: *mut FILE) -> c_int { if (*f)._err != 0 { 1 } else { 0 } }
6666
6667#[no_mangle]
6668pub unsafe extern "C" fn clearerr(f: *mut FILE) { (*f)._eof = 0; (*f)._err = 0; }
6669
6670#[no_mangle]
6671pub unsafe extern "C" fn fileno(f: *mut FILE) -> c_int { (*f).fd }
6672
6673// ============================================================
6674// fseek / ftell / rewind / fseeko / ftello / fgetpos / fsetpos
6675// ============================================================
6676
6677#[no_mangle]
6678pub unsafe extern "C" fn fseek(stream: *mut FILE, offset: c_long, whence: c_int) -> c_int {
6679    fseeko(stream, offset as i64, whence)
6680}
6681
6682#[no_mangle]
6683pub unsafe extern "C" fn ftell(stream: *mut FILE) -> c_long {
6684    ftello(stream) as c_long
6685}
6686
6687#[no_mangle]
6688pub unsafe extern "C" fn rewind(stream: *mut FILE) {
6689    fseek(stream, 0, SEEK_SET);
6690    (*stream)._eof = 0;
6691    (*stream)._err = 0;
6692}
6693
6694#[no_mangle]
6695pub unsafe extern "C" fn fseeko(stream: *mut FILE, offset: i64, whence: c_int) -> c_int {
6696    if whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END {
6697        ERRNO!() = EINVAL;
6698        return -1;
6699    }
6700    let f = &mut *stream;
6701    let mut adj_offset = offset;
6702    if whence == SEEK_CUR {
6703        if !f.rpos.is_null() {
6704            adj_offset -= f.rend as i64 - f.rpos as i64;
6705        }
6706        adj_offset -= f.ungotten_count as i64;
6707    }
6708    if !f.wpos.is_null() && f.wpos != f.wbase {
6709        if flush_buf(stream) != 0 { return -1; }
6710    }
6711    f.wpos = core::ptr::null_mut();
6712    f.wbase = core::ptr::null_mut();
6713    f.wend = core::ptr::null_mut();
6714    if let Some(sfunc) = f.seek_fn {
6715        let r = sfunc(stream, adj_offset, whence);
6716        if r < 0 { return -1; }
6717    } else {
6718        ERRNO!() = EINVAL;
6719        return -1;
6720    }
6721    f.rpos = core::ptr::null_mut();
6722    f.rend = core::ptr::null_mut();
6723    f._eof = 0;
6724    f.ungotten_count = 0;
6725    0
6726}
6727
6728#[no_mangle]
6729pub unsafe extern "C" fn ftello(stream: *mut FILE) -> i64 {
6730    let f = &mut *stream;
6731    if f.flags & F_APP != 0 && !f.wpos.is_null() && f.wpos != f.wbase {
6732        if fflush(stream) != 0 { return -1; }
6733    }
6734    let sfunc = match f.seek_fn { Some(s) => s, None => return -1 };
6735    let pos = sfunc(stream, 0,
6736        if (f.flags & F_APP) != 0 && !f.wpos.is_null() && f.wpos != f.wbase { SEEK_END } else { SEEK_CUR });
6737    if pos < 0 { return -1; }
6738    let mut pos = pos;
6739    if !f.rpos.is_null() {
6740        pos += f.rpos as i64 - f.rend as i64;
6741    } else if !f.wbase.is_null() {
6742        pos += f.wpos as i64 - f.wbase as i64;
6743    }
6744    pos - f.ungotten_count as i64
6745}
6746
6747#[no_mangle]
6748pub unsafe extern "C" fn fgetpos(stream: *mut FILE, pos: *mut i64) -> c_int {
6749    let off = ftello(stream);
6750    if off < 0 { return -1; }
6751    *pos = off;
6752    0
6753}
6754
6755#[no_mangle]
6756pub unsafe extern "C" fn fsetpos(stream: *mut FILE, pos: *const i64) -> c_int {
6757    fseeko(stream, *pos, SEEK_SET)
6758}
6759
6760// ============================================================
6761// fgetc / getc / getchar / ungetc
6762// ============================================================
6763
6764#[no_mangle]
6765pub unsafe extern "C" fn fgetc(stream: *mut FILE) -> c_int {
6766    let f = &mut *stream;
6767    if f.ungotten_count > 0 {
6768        f.ungotten_count -= 1;
6769        return f.ungotten[f.ungotten_count as usize];
6770    }
6771    if !f.rpos.is_null() && f.rpos < f.rend {
6772        let c = *f.rpos;
6773        f.rpos = f.rpos.add(1);
6774        return c as c_int;
6775    }
6776    if f._eof != 0 { return -1; }
6777    if let Some(rfunc) = f.read_fn {
6778        // Fill the stdio buffer in one read. Reading one byte per syscall is
6779        // not only slow, it is wrong for procfs files that only return their
6780        // contents on the first read: a second one-byte read sees EOF.
6781        if !f.buf.is_null() && f.buf_size > 1 {
6782            let n = rfunc(stream, f.buf, f.buf_size);
6783            if n == 0 {
6784                if f.flags & F_ERR != 0 { f._err = 1; } else { f._eof = 1; }
6785                return -1;
6786            }
6787            f.rpos = f.buf;
6788            f.rend = f.buf.add(n);
6789            let c = *f.rpos;
6790            f.rpos = f.rpos.add(1);
6791            return c as c_int;
6792        }
6793        let mut buf = [0u8; 1];
6794        let n = rfunc(stream, buf.as_mut_ptr(), 1);
6795        if n == 0 {
6796            if f.flags & F_ERR != 0 { f._err = 1; } else { f._eof = 1; }
6797            return -1;
6798        }
6799        buf[0] as c_int
6800    } else {
6801        f._eof = 1;
6802        -1
6803    }
6804}
6805
6806#[no_mangle]
6807pub unsafe extern "C" fn getc(stream: *mut FILE) -> c_int { fgetc(stream) }
6808
6809#[no_mangle]
6810pub unsafe extern "C" fn getchar() -> c_int { fgetc(stdin) }
6811
6812#[no_mangle]
6813pub unsafe extern "C" fn ungetc(c: c_int, stream: *mut FILE) -> c_int {
6814    if c == -1 { return -1; }
6815    let f = &mut *stream;
6816    if f.ungotten_count >= UNGET as c_int { return -1; }
6817    f.ungotten[f.ungotten_count as usize] = c;
6818    f.ungotten_count += 1;
6819    f._eof = 0;
6820    c
6821}
6822
6823// ============================================================
6824// fgets / fread
6825// ============================================================
6826
6827#[no_mangle]
6828pub unsafe extern "C" fn fgets(s: *mut c_char, n: c_int, stream: *mut FILE) -> *mut c_char {
6829    if n <= 0 { return null_mut(); }
6830    let max = (n - 1) as usize;
6831    let mut i = 0usize;
6832    while i < max {
6833        let c = fgetc(stream);
6834        if c == -1 { if i == 0 { return null_mut(); } break; }
6835        *s.add(i) = c as c_char;
6836        i += 1;
6837        if c == b'\n' as c_int { break; }
6838    }
6839    *s.add(i) = 0;
6840    s
6841}
6842
6843// ponytail: gets is dangerous, kept for POSIX compliance
6844#[no_mangle]
6845pub unsafe extern "C" fn gets(s: *mut c_char) -> *mut c_char {
6846    let mut i = 0usize;
6847    loop {
6848        let c = fgetc(stdin);
6849        if c == -1 { if i == 0 { return null_mut(); } break; }
6850        if c == b'\n' as c_int { break; }
6851        *s.add(i) = c as c_char;
6852        i += 1;
6853    }
6854    *s.add(i) = 0;
6855    s
6856}
6857
6858#[no_mangle]
6859pub unsafe extern "C" fn fopen64(filename: *const c_char, mode: *const c_char) -> *mut FILE {
6860    fopen(filename, mode)
6861}
6862
6863// ponytail: perror - minimal, just prints "msg: errno\n"
6864const PERROR_BUF_SIZE: usize = 64;
6865static mut PERROR_BUF: [u8; PERROR_BUF_SIZE] = [0; PERROR_BUF_SIZE];
6866
6867#[no_mangle]
6868pub unsafe extern "C" fn perror(msg: *const c_char) {
6869    let fd = (*stderr).fd;
6870    if !msg.is_null() && *msg != 0 {
6871        let len = strlen(msg as *const c_char);
6872        write_str(fd, msg as *const u8, len);
6873        write_str(fd, b": ".as_ptr(), 2);
6874    }
6875    let e = ERRNO!();
6876    let text = strerror(e) as *const u8;
6877    write_str(fd, text, strlen(text as *const c_char));
6878    write_str(fd, b"\n".as_ptr(), 1);
6879}
6880
6881#[no_mangle]
6882pub unsafe extern "C" fn fread(
6883    ptr: *mut c_void, size: SizeT, nmemb: SizeT, stream: *mut FILE,
6884) -> SizeT {
6885    if size == 0 || nmemb == 0 { return 0; }
6886    let total = size * nmemb;
6887    let mut rd = 0usize;
6888    while rd < total {
6889        let c = fgetc(stream);
6890        if c == -1 { break; }
6891        *(ptr as *mut u8).add(rd) = c as u8;
6892        rd += 1;
6893    }
6894    rd / size
6895}
6896
6897// ============================================================
6898// puts / fputs / fputc / putc / putchar / fwrite
6899// ============================================================
6900
6901unsafe fn write_str(fd: c_int, s: *const u8, len: usize) {
6902    let mut written = 0usize;
6903    while written < len {
6904        let n = sys_write(fd as i64, s.add(written), len - written);
6905        if n <= 0 { break; }
6906        written += n as usize;
6907    }
6908}
6909
6910#[no_mangle]
6911unsafe fn __overflow(f: *mut FILE, c: c_int) -> c_int {
6912    if (*f).wpos.is_null() {
6913        (*f).wbase = (*f).buf;
6914        (*f).wpos = (*f).buf;
6915        (*f).wend = (*f).buf.add((*f).buf_size);
6916    }
6917    if flush_buf(f) == -1 { return -1; }
6918    if (*f).wpos >= (*f).wend {
6919        if let Some(wfunc) = (*f).write_fn {
6920            let byte = c as u8;
6921            let ret = wfunc(f, &byte as *const u8, 1);
6922            if ret == 0 { return -1; }
6923        }
6924        return c;
6925    }
6926    *(*f).wpos = c as u8;
6927    (*f).wpos = (*f).wpos.add(1);
6928    if c == (*f).lbf || (*f).wpos >= (*f).wend { let _ = flush_buf(f); }
6929    c
6930}
6931
6932#[no_mangle]
6933pub unsafe extern "C" fn puts(s: *const c_char) -> c_int {
6934    let len = strlen(s as *const c_char);
6935    let _ = fwrite(s as *const c_void, 1, len, stdout);
6936    let _ = fwrite(b"\n".as_ptr() as *const c_void, 1, 1, stdout);
6937    let f = &mut *stdout;
6938    if f.lbf >= 0 { let _ = flush_buf(stdout); }
6939    (len + 1) as c_int
6940}
6941
6942#[no_mangle]
6943pub unsafe extern "C" fn fputs(s: *const c_char, stream: *mut FILE) -> c_int {
6944    let len = strlen(s as *const c_char);
6945    fwrite(s as *const c_void, 1, len, stream) as c_int
6946}
6947
6948#[no_mangle]
6949pub unsafe extern "C" fn fputc(c: c_int, stream: *mut FILE) -> c_int {
6950    let f = &mut *stream;
6951    if f.wpos.is_null() || f.wpos >= f.wend {
6952        if __overflow(f, c) == -1 { return -1; }
6953        return c;
6954    }
6955    *f.wpos = c as u8;
6956    f.wpos = f.wpos.add(1);
6957    if c == f.lbf || f.wpos >= f.wend { let _ = flush_buf(f); }
6958    c
6959}
6960
6961#[no_mangle]
6962pub unsafe extern "C" fn putc(c: c_int, stream: *mut FILE) -> c_int { fputc(c, stream) }
6963
6964#[no_mangle]
6965pub unsafe extern "C" fn putchar(c: c_int) -> c_int { fputc(c, stdout) }
6966
6967#[no_mangle]
6968pub unsafe extern "C" fn fwrite(
6969    ptr: *const c_void, size: SizeT, nmemb: SizeT, stream: *mut FILE,
6970) -> SizeT {
6971    if size == 0 || nmemb == 0 { return 0; }
6972    let total = size * nmemb;
6973    let f = &mut *stream;
6974    if f.buf_size == 0 {
6975        if let Some(wfunc) = f.write_fn {
6976            let ret = wfunc(stream, ptr as *const u8, total);
6977            return ret / size;
6978        }
6979        return 0;
6980    }
6981    if f.wpos.is_null() {
6982        f.wbase = f.buf;
6983        f.wpos = f.buf;
6984        f.wend = f.buf.add(f.buf_size);
6985    }
6986    let mut remaining = total;
6987    let mut src = ptr as *const u8;
6988    while remaining > 0 {
6989        let space = f.wend as usize - f.wpos as usize;
6990        if space == 0 {
6991            if flush_buf(f as *mut FILE) == -1 { break; }
6992            continue;
6993        }
6994        let n = if remaining < space { remaining } else { space };
6995        core::ptr::copy_nonoverlapping(src, f.wpos, n);
6996        f.wpos = f.wpos.add(n);
6997        src = src.add(n);
6998        remaining -= n;
6999        if f.wpos >= f.wend { let _ = flush_buf(f as *mut FILE); }
7000    }
7001    if f.lbf >= 0 { let _ = flush_buf(f as *mut FILE); }
7002    (total - remaining) / size
7003}
7004
7005// ============================================================
7006// printf / fprintf / vprintf / vfprintf / dprintf / vdprintf
7007// ============================================================
7008
7009unsafe fn wide_to_mb(dst: *mut u8, cap: usize, src: *const wchar_t) -> usize {
7010    let mut o = 0usize;
7011    let mut i = 0usize;
7012    let mut st: c_uint = 0;
7013    while *src.add(i) != 0 {
7014        let mut tmp = [0u8; 8];
7015        let n = wcrtomb(tmp.as_mut_ptr() as *mut c_char, *src.add(i), &mut st);
7016        if n == !0usize || n == 0 { break; }
7017        if o + n > cap { break; }
7018        core::ptr::copy_nonoverlapping(tmp.as_ptr(), dst.add(o), n);
7019        o += n;
7020        i += 1;
7021    }
7022    o
7023}
7024
7025macro_rules! impl_format {
7026    ($fmt:expr, $args:expr, $write_char:expr, $write_str:expr) => {{
7027        let fmt = $fmt;
7028        let args = &mut $args;
7029        let mut count: usize = 0;
7030        let mut i = 0usize;
7031        loop {
7032            let c = *fmt.add(i) as u8;
7033            if c == 0 { break; }
7034            if c != b'%' {
7035                ($write_char)(c);
7036                count += 1; i += 1; continue;
7037            }
7038            i += 1;
7039            // Parse flags
7040            let mut flags: u8 = 0;
7041            loop {
7042                let fc = *fmt.add(i) as u8;
7043                match fc {
7044                    b'-' => { flags |= FLAG_MINUS; i += 1; }
7045                    b'+' => { flags |= FLAG_PLUS; i += 1; }
7046                    b' ' => { flags |= FLAG_SPACE; i += 1; }
7047                    b'0' => { flags |= FLAG_ZERO; i += 1; }
7048                    b'#' => { flags |= FLAG_HASH; i += 1; }
7049                    _ => break,
7050                }
7051            }
7052            // Parse width
7053            let mut width: usize = 0;
7054            if *fmt.add(i) as u8 == b'*' {
7055                width = args.next_arg::<c_int>() as usize; i += 1;
7056            } else {
7057                while (*fmt.add(i) as u8) >= b'0' && (*fmt.add(i) as u8) <= b'9' {
7058                    width = width * 10 + ((*fmt.add(i) as u8) - b'0') as usize; i += 1;
7059                }
7060            }
7061            // Parse precision
7062            let mut precision: i32 = -1;
7063            if *fmt.add(i) as u8 == b'.' {
7064                i += 1;
7065                if *fmt.add(i) as u8 == b'*' {
7066                    precision = args.next_arg::<c_int>(); i += 1;
7067                } else {
7068                    precision = 0;
7069                    while (*fmt.add(i) as u8) >= b'0' && (*fmt.add(i) as u8) <= b'9' {
7070                        precision = precision * 10 + ((*fmt.add(i) as u8) - b'0') as i32; i += 1;
7071                    }
7072                }
7073            }
7074            // Parse length modifier
7075            let len_mod = *fmt.add(i) as u8;
7076            let spec: u8;
7077            if len_mod == b'h' || len_mod == b'l' || len_mod == b'j' || len_mod == b'z' || len_mod == b't' || len_mod == b'L' {
7078                i += 1;
7079                let len_mod2 = *fmt.add(i) as u8;
7080                if (len_mod == b'h' && len_mod2 == b'h') || (len_mod == b'l' && len_mod2 == b'l') {
7081                    i += 1;
7082                    spec = *fmt.add(i) as u8;
7083                    // Handle double-length modifiers
7084                    match (len_mod, len_mod2, spec) {
7085                        (b'h', b'h', b'n') => {
7086                            let p = args.next_arg::<*mut c_void>();
7087                            if !p.is_null() { *(p as *mut u8) = count as u8; }
7088                        }
7089                        (b'l', b'l', b'n') => {
7090                            let p = args.next_arg::<*mut c_void>();
7091                            if !p.is_null() { *(p as *mut c_longlong) = count as c_longlong; }
7092                        }
7093                        (b'l', b'l', b'd') | (b'l', b'l', b'i') => {
7094                            let val = args.next_arg::<c_longlong>();
7095                            let neg = val < 0;
7096                            let abs = if neg { val.wrapping_neg() as u64 } else { val as u64 };
7097                            let b = format_u64(abs);
7098                            let sign = if neg { Some(b'-') } else if flags & FLAG_PLUS != 0 { Some(b'+') } else if flags & FLAG_SPACE != 0 { Some(b' ') } else { None };
7099                            let mut fbuf = [0u8; 32];
7100                            let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, sign, None, precision, width, flags, false);
7101                            ($write_str)(fbuf.as_ptr(), len); count += len;
7102                        }
7103                        (b'l', b'l', b'u') => {
7104                            let val = args.next_arg::<c_ulonglong>();
7105                            let b = format_u64(val as u64);
7106                            let mut fbuf = [0u8; 32];
7107                            let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, None, precision, width, flags, false);
7108                            ($write_str)(fbuf.as_ptr(), len); count += len;
7109                        }
7110                        (b'l', b'l', b'o') => {
7111                            let val = args.next_arg::<c_ulonglong>();
7112                            let b = format_octal(val as u64, flags & FLAG_HASH != 0);
7113                            let mut fbuf = [0u8; 32];
7114                            let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, None, precision, width, flags, flags & FLAG_HASH != 0);
7115                            ($write_str)(fbuf.as_ptr(), len); count += len;
7116                        }
7117                        (b'l', b'l', b'x') => {
7118                            let val = args.next_arg::<c_ulonglong>();
7119                            let b = format_hex(val as u64, false);
7120                            let prefix = if flags & FLAG_HASH != 0 && !(b.1 == 1 && b.0[0] == b'0') { Some(b'x') } else { None };
7121                            let mut fbuf = [0u8; 32];
7122                            let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, prefix, precision, width, flags, false);
7123                            ($write_str)(fbuf.as_ptr(), len); count += len;
7124                        }
7125                        (b'l', b'l', b'X') => {
7126                            let val = args.next_arg::<c_ulonglong>();
7127                            let b = format_hex(val as u64, true);
7128                            let prefix = if flags & FLAG_HASH != 0 && !(b.1 == 1 && b.0[0] == b'0') { Some(b'X') } else { None };
7129                            let mut fbuf = [0u8; 32];
7130                            let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, prefix, precision, width, flags, false);
7131                            ($write_str)(fbuf.as_ptr(), len); count += len;
7132                        }
7133                        _ => { ($write_char)(b'%'); ($write_char)(len_mod); ($write_char)(len_mod2); ($write_char)(spec); count += 4; }
7134                    }
7135                } else {
7136                    spec = len_mod2;
7137                    // size_t/ptrdiff_t are 64-bit on all supported targets;
7138                    // treat %z/%t integer conversions like %l.
7139                    let lm = if (len_mod == b'z' || len_mod == b't') && spec != b'n' { b'l' } else { len_mod };
7140                    match (lm, spec) {
7141                        (b'h', b'n') => {
7142                            let p = args.next_arg::<*mut c_void>();
7143                            if !p.is_null() { *(p as *mut u16) = count as u16; }
7144                        }
7145                        (b'l', b'n') => {
7146                            let p = args.next_arg::<*mut c_void>();
7147                            if !p.is_null() { *(p as *mut c_long) = count as c_long; }
7148                        }
7149                        (b'z', b'n') | (b't', b'n') => {
7150                            let p = args.next_arg::<*mut c_void>();
7151                            if !p.is_null() { *(p as *mut usize) = count; }
7152                        }
7153                        (b'j', b'n') => {
7154                            let p = args.next_arg::<*mut c_void>();
7155                            if !p.is_null() { *(p as *mut c_ulonglong) = count as c_ulonglong; }
7156                        }
7157                        (b'l', b'd') | (b'l', b'i') => {
7158                            let val = args.next_arg::<c_long>();
7159                            let neg = val < 0;
7160                            let abs = if neg { val.wrapping_neg() as u64 } else { val as u64 };
7161                            let b = format_u64(abs);
7162                            let sign = if neg { Some(b'-') } else if flags & FLAG_PLUS != 0 { Some(b'+') } else if flags & FLAG_SPACE != 0 { Some(b' ') } else { None };
7163                            let mut fbuf = [0u8; 32];
7164                            let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, sign, None, precision, width, flags, false);
7165                            ($write_str)(fbuf.as_ptr(), len); count += len;
7166                        }
7167                        (b'l', b'u') => {
7168                            let val = args.next_arg::<c_ulong>();
7169                            let b = format_u64(val as u64);
7170                            let mut fbuf = [0u8; 32];
7171                            let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, None, precision, width, flags, false);
7172                            ($write_str)(fbuf.as_ptr(), len); count += len;
7173                        }
7174                        (b'l', b's') => {
7175                            let p = args.next_arg::<*const wchar_t>();
7176                            if p.is_null() {
7177                                let slen = if precision >= 0 { (precision as usize).min(6) } else { 6 };
7178                                let pad = width.saturating_sub(slen);
7179                                if flags & FLAG_MINUS != 0 { ($write_str)(b"(null)".as_ptr(), slen); for _ in 0..pad { ($write_char)(b' '); } }
7180                                else { for _ in 0..pad { ($write_char)(b' '); } ($write_str)(b"(null)".as_ptr(), slen); }
7181                                count += slen + pad;
7182                            } else {
7183                                let mut mb = [0u8; 4096];
7184                                let mut n = wide_to_mb(mb.as_mut_ptr(), mb.len(), p);
7185                                if precision >= 0 && (precision as usize) < n { n = precision as usize; }
7186                                let pad = width.saturating_sub(n);
7187                                if flags & FLAG_MINUS != 0 { ($write_str)(mb.as_ptr(), n); for _ in 0..pad { ($write_char)(b' '); } }
7188                                else { for _ in 0..pad { ($write_char)(b' '); } ($write_str)(mb.as_ptr(), n); }
7189                                count += n + pad;
7190                            }
7191                        }
7192                        (b'l', b'c') => {
7193                            let ch = args.next_arg::<c_int>();
7194                            let mut mb = [0u8; 8];
7195                            let mut st: c_uint = 0;
7196                            let n = wcrtomb(mb.as_mut_ptr() as *mut c_char, ch, &mut st);
7197                            if n != !0usize { ($write_str)(mb.as_ptr(), n); count += n; }
7198                        }
7199                        (b'l', b'x') => {
7200                            let val = args.next_arg::<c_ulong>();
7201                            let b = format_hex(val as u64, false);
7202                            let prefix = if flags & FLAG_HASH != 0 && !(b.1 == 1 && b.0[0] == b'0') { Some(b'x') } else { None };
7203                            let mut fbuf = [0u8; 32];
7204                            let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, prefix, precision, width, flags, false);
7205                            ($write_str)(fbuf.as_ptr(), len); count += len;
7206                        }
7207                        (b'l', b'X') => {
7208                            let val = args.next_arg::<c_ulong>();
7209                            let b = format_hex(val as u64, true);
7210                            let prefix = if flags & FLAG_HASH != 0 && !(b.1 == 1 && b.0[0] == b'0') { Some(b'X') } else { None };
7211                            let mut fbuf = [0u8; 32];
7212                            let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, prefix, precision, width, flags, false);
7213                            ($write_str)(fbuf.as_ptr(), len); count += len;
7214                        }
7215                        (b'l', b'o') => {
7216                            let val = args.next_arg::<c_ulong>();
7217                            let b = format_octal(val as u64, flags & FLAG_HASH != 0);
7218                            let mut fbuf = [0u8; 32];
7219                            let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, None, precision, width, flags, flags & FLAG_HASH != 0);
7220                            ($write_str)(fbuf.as_ptr(), len); count += len;
7221                        }
7222                        (b'l', b'f') | (b'l', b'F') | (b'l', b'e') | (b'l', b'E')
7223                        | (b'l', b'g') | (b'l', b'G') | (b'l', b'a') | (b'l', b'A') => {
7224                            let val = args.next_arg::<f64>();
7225                            let ucase = spec == b'F' || spec == b'E' || spec == b'G' || spec == b'A';
7226                            let ftype = match spec | 0x20 { b'f' => FMT_F, b'e' => FMT_E, b'g' => FMT_G, b'a' => FMT_A, _ => FMT_F };
7227                            let mut fbuf = [0u8; 4224];
7228                            let flen = format_f64_full(fbuf.as_mut_ptr(), val, ftype, precision, flags, ucase);
7229                            let total = apply_width_flags(fbuf.as_mut_ptr().add(4096), fbuf.as_ptr(), flen, width, flags);
7230                            core::ptr::copy_nonoverlapping(fbuf.as_ptr().add(4096), fbuf.as_mut_ptr(), total);
7231                            ($write_str)(fbuf.as_ptr(), total); count += total;
7232                        }
7233                        (b'L', b'f') | (b'L', b'F') | (b'L', b'e') | (b'L', b'E')
7234                        | (b'L', b'g') | (b'L', b'G') | (b'L', b'a') | (b'L', b'A') => {
7235                            #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
7236                            let val = {
7237                                let lo: u64 = args.next_arg::<u64>();
7238                                let hi: u64 = args.next_arg::<u64>();
7239                                let combined: u128 = ((hi as u128) << 64) | (lo as u128);
7240                                f128::from_bits(combined) as f64
7241                            };
7242                            #[cfg(target_arch = "x86_64")]
7243                            let val = args.next_arg::<f64>();
7244                            let ucase = spec == b'F' || spec == b'E' || spec == b'G' || spec == b'A';
7245                            let ftype = match spec | 0x20 { b'f' => FMT_F, b'e' => FMT_E, b'g' => FMT_G, b'a' => FMT_A, _ => FMT_F };
7246                            let mut fbuf = [0u8; 4224];
7247                            let flen = format_f64_full(fbuf.as_mut_ptr(), val, ftype, precision, flags, ucase);
7248                            let total = apply_width_flags(fbuf.as_mut_ptr().add(4096), fbuf.as_ptr(), flen, width, flags);
7249                            core::ptr::copy_nonoverlapping(fbuf.as_ptr().add(4096), fbuf.as_mut_ptr(), total);
7250                            ($write_str)(fbuf.as_ptr(), total); count += total;
7251                        }
7252                        _ => { ($write_char)(b'%'); ($write_char)(len_mod); ($write_char)(spec); count += 3; }
7253                    }
7254                }
7255            } else {
7256                spec = *fmt.add(i) as u8;
7257                match spec {
7258                    b's' => {
7259                        let s = args.next_arg::<*const c_char>();
7260                        if s.is_null() {
7261                            let slen = if precision >= 0 { (precision as usize).min(6) } else { 6 };
7262                            let padded_len = if width > slen { width } else { slen };
7263                            let pad = padded_len - slen;
7264                            if flags & FLAG_MINUS != 0 {
7265                                ($write_str)(b"(null)".as_ptr(), slen);
7266                                for _ in 0..pad { ($write_char)(b' '); }
7267                            } else {
7268                                for _ in 0..pad { ($write_char)(b' '); }
7269                                ($write_str)(b"(null)".as_ptr(), slen);
7270                            }
7271                            count += padded_len;
7272                        } else {
7273                            let full_len = strlen(s as *const c_char);
7274                            let slen = if precision >= 0 { (precision as usize).min(full_len) } else { full_len };
7275                            let padded_len = if width > slen { width } else { slen };
7276                            let pad = padded_len - slen;
7277                            if flags & FLAG_MINUS != 0 {
7278                                ($write_str)(s as *const u8, slen);
7279                                for _ in 0..pad { ($write_char)(b' '); }
7280                            } else {
7281                                for _ in 0..pad { ($write_char)(b' '); }
7282                                ($write_str)(s as *const u8, slen);
7283                            }
7284                            count += padded_len;
7285                        }
7286                    }
7287                    b'd' | b'i' => {
7288                        let d = args.next_arg::<c_int>();
7289                        let neg = d < 0;
7290                        let abs = if neg { d.wrapping_neg() as u64 } else { d as u64 };
7291                        let b = format_u64(abs);
7292                        let sign = if neg { Some(b'-') } else if flags & FLAG_PLUS != 0 { Some(b'+') } else if flags & FLAG_SPACE != 0 { Some(b' ') } else { None };
7293                        let mut fbuf = [0u8; 32];
7294                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, sign, None, precision, width, flags, false);
7295                        ($write_str)(fbuf.as_ptr(), len); count += len;
7296                    }
7297                    b'u' => {
7298                        let u = args.next_arg::<c_uint>();
7299                        let b = format_u64(u as u64);
7300                        let mut fbuf = [0u8; 32];
7301                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, None, precision, width, flags, false);
7302                        ($write_str)(fbuf.as_ptr(), len); count += len;
7303                    }
7304                    b'x' => {
7305                        let x = args.next_arg::<c_uint>();
7306                        let b = format_hex(x as u64, false);
7307                        let prefix = if flags & FLAG_HASH != 0 && !(b.1 == 1 && b.0[0] == b'0') { Some(b'x') } else { None };
7308                        let mut fbuf = [0u8; 32];
7309                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, prefix, precision, width, flags, false);
7310                        ($write_str)(fbuf.as_ptr(), len); count += len;
7311                    }
7312                    b'X' => {
7313                        let x = args.next_arg::<c_uint>();
7314                        let b = format_hex(x as u64, true);
7315                        let prefix = if flags & FLAG_HASH != 0 && !(b.1 == 1 && b.0[0] == b'0') { Some(b'X') } else { None };
7316                        let mut fbuf = [0u8; 32];
7317                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, prefix, precision, width, flags, false);
7318                        ($write_str)(fbuf.as_ptr(), len); count += len;
7319                    }
7320                    b'o' => {
7321                        let o = args.next_arg::<c_uint>();
7322                        let b = format_octal(o as u64, flags & FLAG_HASH != 0);
7323                        let mut fbuf = [0u8; 32];
7324                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, None, precision, width, flags, flags & FLAG_HASH != 0);
7325                        ($write_str)(fbuf.as_ptr(), len); count += len;
7326                    }
7327                    b'c' => {
7328                        let ch = args.next_arg::<c_int>();
7329                        if width > 1 {
7330                            let pad = width - 1;
7331                            if flags & FLAG_MINUS != 0 {
7332                                ($write_char)(ch as u8); count += 1;
7333                                for _ in 0..pad { ($write_char)(b' '); count += 1; }
7334                            } else {
7335                                for _ in 0..pad { ($write_char)(b' '); count += 1; }
7336                                ($write_char)(ch as u8); count += 1;
7337                            }
7338                        } else {
7339                            ($write_char)(ch as u8); count += 1;
7340                        }
7341                    }
7342                    b'p' => {
7343                        let p = args.next_arg::<*const c_void>();
7344                        let mut fbuf = [0u8; 20];
7345                        fbuf[0] = b'0'; fbuf[1] = b'x';
7346                        let b = format_hex(p as u64, false);
7347                        core::ptr::copy_nonoverlapping(b.0.as_ptr(), fbuf.as_mut_ptr().add(2), b.1);
7348                        ($write_str)(fbuf.as_ptr(), b.1 + 2); count += b.1 + 2;
7349                    }
7350                    b'f' | b'F' | b'e' | b'E' | b'g' | b'G' | b'a' | b'A' => {
7351                        let val = args.next_arg::<f64>();
7352                        let ucase = spec == b'F' || spec == b'E' || spec == b'G' || spec == b'A';
7353                        let ftype = match spec | 0x20 { b'f' => FMT_F, b'e' => FMT_E, b'g' => FMT_G, b'a' => FMT_A, _ => FMT_F };
7354                        let mut fbuf = [0u8; 4224];
7355                        let flen = format_f64_full(fbuf.as_mut_ptr(), val, ftype, precision, flags, ucase);
7356                        let total = apply_width_flags(fbuf.as_mut_ptr().add(4096), fbuf.as_ptr(), flen, width, flags);
7357                        core::ptr::copy_nonoverlapping(fbuf.as_ptr().add(4096), fbuf.as_mut_ptr(), total);
7358                        ($write_str)(fbuf.as_ptr(), total); count += total;
7359                    }
7360                    b'%' => { ($write_char)(b'%'); count += 1; }
7361                    b'n' => {
7362                        let p = args.next_arg::<*mut c_void>();
7363                        if !p.is_null() { *(p as *mut c_int) = count as c_int; }
7364                    }
7365                    _ => { ($write_char)(b'%'); ($write_char)(spec); count += 2; }
7366                }
7367            }
7368            i += 1;
7369        }
7370        count as c_int
7371    }};
7372}
7373
7374unsafe fn format_u64(mut val: u64) -> ([u8; 20], usize) {
7375    let mut buf = [0u8; 20];
7376    if val == 0 { buf[0] = b'0'; return (buf, 1); }
7377    let mut tmp = [0u8; 20];
7378    let mut pos = 20;
7379    while val > 0 { pos -= 1; tmp[pos] = b'0' + (val % 10) as u8; val /= 10; }
7380    let len = 20 - pos;
7381    core::ptr::copy_nonoverlapping(tmp.as_ptr().add(pos), buf.as_mut_ptr(), len);
7382    (buf, len)
7383}
7384
7385unsafe fn format_i64(val: i64) -> ([u8; 21], usize) {
7386    let mut buf = [0u8; 21];
7387    if val < 0 {
7388        buf[0] = b'-';
7389        let (inner, len) = format_u64((-(val as i128)) as u64);
7390        core::ptr::copy_nonoverlapping(inner.as_ptr(), buf.as_mut_ptr().add(1), len);
7391        (buf, len + 1)
7392    } else {
7393        let (inner, len) = format_u64(val as u64);
7394        core::ptr::copy_nonoverlapping(inner.as_ptr(), buf.as_mut_ptr(), len);
7395        (buf, len)
7396    }
7397}
7398
7399unsafe fn format_hex(mut val: u64, uppercase: bool) -> ([u8; 16], usize) {
7400    let digits = if uppercase { b"0123456789ABCDEF" } else { b"0123456789abcdef" };
7401    let mut buf = [0u8; 16];
7402    if val == 0 { buf[0] = b'0'; return (buf, 1); }
7403    let mut tmp = [0u8; 16];
7404    let mut pos = 16;
7405    while val > 0 { pos -= 1; tmp[pos] = digits[(val & 0xf) as usize]; val >>= 4; }
7406    let len = 16 - pos;
7407    core::ptr::copy_nonoverlapping(tmp.as_ptr().add(pos), buf.as_mut_ptr(), len);
7408    (buf, len)
7409}
7410
7411unsafe fn format_octal(mut val: u64, alt: bool) -> ([u8; 24], usize) {
7412    let mut buf = [0u8; 24];
7413    if val == 0 {
7414        buf[0] = b'0';
7415        return (buf, 1);
7416    }
7417    let mut tmp = [0u8; 24];
7418    let mut pos = 24;
7419    while val > 0 {
7420        pos -= 1;
7421        tmp[pos] = b'0' + (val & 7) as u8;
7422        val >>= 3;
7423    }
7424    let len = 24 - pos;
7425    if alt {
7426        tmp[pos - 1] = b'0';
7427        core::ptr::copy_nonoverlapping(tmp.as_ptr().add(pos - 1), buf.as_mut_ptr(), len + 1);
7428        (buf, len + 1)
7429    } else {
7430        core::ptr::copy_nonoverlapping(tmp.as_ptr().add(pos), buf.as_mut_ptr(), len);
7431        (buf, len)
7432    }
7433}
7434
7435unsafe fn format_int(
7436    dst: *mut u8,
7437    digits: *const u8,
7438    digits_len: usize,
7439    sign: Option<u8>,
7440    prefix: Option<u8>,
7441    precision: i32,
7442    width: usize,
7443    flags: u8,
7444    octal_alt: bool,
7445) -> usize {
7446    let num_digits = if precision == 0 && digits_len == 1 && *digits == b'0' && !octal_alt {
7447        0
7448    } else {
7449        digits_len
7450    };
7451    let prec = precision as usize;
7452    let zero_pad = if precision >= 0 && prec > num_digits { prec - num_digits } else { 0 };
7453
7454    let sign_len = sign.map_or(0, |_| 1);
7455    let prefix_len = prefix.map_or(0, |_| 2);
7456    let content_len = sign_len + prefix_len + zero_pad + num_digits;
7457
7458    let mut buf = [0u8; 32];
7459    let mut pos = 0usize;
7460    if let Some(c) = sign { buf[pos] = c; pos += 1; }
7461    if let Some(c) = prefix { buf[pos] = b'0'; pos += 1; buf[pos] = c; pos += 1; }
7462    for _ in 0..zero_pad { buf[pos] = b'0'; pos += 1; }
7463    if num_digits > 0 {
7464        core::ptr::copy_nonoverlapping(digits, buf.as_mut_ptr().add(pos), num_digits);
7465    }
7466
7467    if content_len >= width {
7468        core::ptr::copy_nonoverlapping(buf.as_ptr(), dst, content_len);
7469        return content_len;
7470    }
7471    let pad = width - content_len;
7472    if flags & FLAG_MINUS != 0 {
7473        core::ptr::copy_nonoverlapping(buf.as_ptr(), dst, content_len);
7474        for i in 0..pad { *dst.add(content_len + i) = b' '; }
7475    } else if flags & FLAG_ZERO != 0 && precision < 0 {
7476        let fixed = sign_len + prefix_len;
7477        core::ptr::copy_nonoverlapping(buf.as_ptr(), dst, fixed);
7478        for i in fixed..width - num_digits { *dst.add(i) = b'0'; }
7479        if num_digits > 0 {
7480            core::ptr::copy_nonoverlapping(digits, dst.add(width - num_digits), num_digits);
7481        }
7482    } else {
7483        for i in 0..pad { *dst.add(i) = b' '; }
7484        core::ptr::copy_nonoverlapping(buf.as_ptr(), dst.add(pad), content_len);
7485    }
7486    width
7487}
7488
7489unsafe fn format_f64(val: f64) -> ([u8; 64], usize) {
7490    let mut buf = [0u8; 64];
7491    let mut pos = 0usize;
7492    let mut v = val;
7493    if v.is_nan() {
7494        let nan = b"nan";
7495        core::ptr::copy_nonoverlapping(nan.as_ptr(), buf.as_mut_ptr(), 3);
7496        return (buf, 3);
7497    }
7498    if v < 0.0 { buf[pos] = b'-'; pos += 1; v = -v; }
7499    if v.is_infinite() {
7500        let inf = b"inf";
7501        core::ptr::copy_nonoverlapping(inf.as_ptr(), buf.as_mut_ptr().add(pos), 3);
7502        return (buf, pos + 3);
7503    }
7504    let int_part = v as u64;
7505    let frac = v - int_part as f64;
7506    let (ibuf, ilen) = format_u64(int_part);
7507    core::ptr::copy_nonoverlapping(ibuf.as_ptr(), buf.as_mut_ptr().add(pos), ilen);
7508    pos += ilen;
7509    buf[pos] = b'.';
7510    pos += 1;
7511    let mut f = frac;
7512    for _ in 0..6 { f *= 10.0; let digit = f as u8; buf[pos] = b'0' + digit; pos += 1; f -= digit as f64; }
7513    (buf, pos)
7514}
7515
7516// Float format specifier type
7517const FMT_F: u8 = 0;
7518const FMT_E: u8 = 1;
7519const FMT_G: u8 = 2;
7520const FMT_A: u8 = 3;
7521
7522const FLAG_HASH: u8 = 1;
7523const FLAG_PLUS: u8 = 2;
7524const FLAG_SPACE: u8 = 4;
7525const FLAG_ZERO: u8 = 8;
7526const FLAG_MINUS: u8 = 16;
7527
7528// === %f formatting helpers ===
7529
7530/// Convert (mant << shift) to decimal string. Returns byte count written to buf.
7531/// mant is a 53-bit significand, shift is the left-shift amount (can be 0..~971).
7532unsafe fn bigint_to_decimal(buf: *mut u8, mant: u64, shift: u32) -> usize {
7533    // Store as little-endian u32 limbs. Max bits: 53 + 971 ≈ 1024 → 32 limbs.
7534    let nlimbs = (2 + (shift + 31) / 32) as usize;
7535    let mut limbs = [0u32; 36];
7536    limbs[0] = mant as u32;
7537    limbs[1] = (mant >> 32) as u32;
7538
7539    let ws = (shift / 32) as usize;
7540    let bs = shift % 32;
7541
7542    // Word-level shift left
7543    if ws > 0 {
7544        let mut i = 1usize;
7545        loop {
7546            limbs[i + ws] = limbs[i];
7547            if i == 0 { break; }
7548            i -= 1;
7549        }
7550        for j in 0..ws { limbs[j] = 0; }
7551    }
7552    // Bit-level shift left
7553    if bs > 0 {
7554        let mut carry = 0u32;
7555        let end = nlimbs.min(ws + 3);
7556        for i in ws..end {
7557            let new_val = (limbs[i] << bs) | carry;
7558            carry = limbs[i] >> (32 - bs);
7559            limbs[i] = new_val;
7560        }
7561    }
7562
7563    // Find highest non-zero limb
7564    let mut used = nlimbs;
7565    while used > 0 && limbs[used - 1] == 0 { used -= 1; }
7566    if used == 0 { *buf = b'0'; return 1; }
7567
7568    // Repeated division by 10 to extract decimal digits
7569    let mut digits = [0u8; 400];
7570    let mut nd = 0usize;
7571    loop {
7572        let mut all_zero = true;
7573        for i in 0..used { if limbs[i] != 0 { all_zero = false; break; } }
7574        if all_zero { break; }
7575        let mut rem = 0u64;
7576        for i in (0..used).rev() {
7577            let val = (rem << 32) | limbs[i] as u64;
7578            limbs[i] = (val / 10) as u32;
7579            rem = val % 10;
7580        }
7581        digits[nd] = rem as u8;
7582        nd += 1;
7583    }
7584    // Write digits in big-endian order
7585    for i in 0..nd { *buf.add(i) = b'0' + digits[nd - 1 - i]; }
7586    nd
7587}
7588
7589/// Compute `count` fractional digits of frac_bits / 2^shift using u128 arithmetic.
7590/// shift must be <= 124. Returns true if any value remains after the last digit.
7591unsafe fn frac_digits_u128(buf: *mut u8, frac_bits: u64, shift: u32, count: usize) -> bool {
7592    let mask: u128 = (1u128 << shift) - 1;
7593    let mut rem = frac_bits as u128;
7594    for i in 0..count {
7595        rem *= 10;
7596        let digit = (rem >> shift) as u8;
7597        rem &= mask;
7598        *buf.add(i) = digit;
7599    }
7600    rem != 0
7601}
7602
7603/// Compute `count` fractional digits of frac_bits / 2^shift using big-integer arithmetic.
7604/// For shift > 124 (up to ~1074). Returns true if any value remains after the last digit.
7605unsafe fn frac_digits_bigint(buf: *mut u8, frac_bits: u64, shift: u32, count: usize) -> bool {
7606    let nlimbs = ((shift + 31) / 32) as usize;
7607    let mut rem = [0u32; 36]; // enough for shift up to 1088
7608    rem[0] = frac_bits as u32;
7609    if nlimbs > 1 { rem[1] = (frac_bits >> 32) as u32; }
7610    // Mask to exactly `shift` bits
7611    let hi_word = (shift / 32) as usize;
7612    let hi_bit = shift % 32;
7613    if hi_bit > 0 && hi_word < nlimbs {
7614        rem[hi_word] &= (1u32 << hi_bit) - 1;
7615    }
7616    // Zero upper limbs beyond shift
7617    let clear_from = if hi_bit > 0 { hi_word + 1 } else { hi_word };
7618    for j in clear_from..nlimbs { rem[j] = 0; }
7619
7620    for i in 0..count {
7621        // Multiply remainder by 10
7622        let mut carry = 0u64;
7623        for j in 0..nlimbs {
7624            let val = rem[j] as u64 * 10 + carry;
7625            rem[j] = val as u32;
7626            carry = val >> 32;
7627        }
7628        // Extract digit from bit position `shift`
7629        let digit: u32;
7630        if hi_bit == 0 {
7631            digit = if hi_word < nlimbs { rem[hi_word] } else { carry as u32 };
7632        } else {
7633            let mut d = if hi_word < nlimbs { rem[hi_word] >> hi_bit } else { 0 };
7634            if hi_word + 1 < nlimbs {
7635                d |= rem[hi_word + 1] << (32 - hi_bit);
7636            } else if carry > 0 {
7637                d |= (carry as u32) << (32 - hi_bit);
7638            }
7639            digit = d;
7640        }
7641        *buf.add(i) = digit.min(9) as u8;
7642        // Clear bits from position `shift` upward (keep remainder < 2^shift)
7643        if hi_bit == 0 {
7644            if hi_word < nlimbs { rem[hi_word] = 0; }
7645        } else {
7646            if hi_word < nlimbs {
7647                rem[hi_word] &= (1u32 << hi_bit) - 1;
7648            }
7649            for j in (hi_word + 1)..nlimbs { rem[j] = 0; }
7650        }
7651    }
7652    // Check if remainder is non-zero
7653    for i in 0..nlimbs { if rem[i] != 0 { return true; } }
7654    false
7655}
7656
7657/// Increment a decimal string stored at buf[0..*len] in-place.
7658unsafe fn increment_decimal(buf: *mut u8, len: &mut usize) {
7659    let mut j = *len;
7660    loop {
7661        if j == 0 {
7662            // Shift right and prepend '1'
7663            let l = *len;
7664            for i in (0..l).rev() { *buf.add(i + 1) = *buf.add(i); }
7665            *buf = b'1';
7666            *len = l + 1;
7667            return;
7668        }
7669        j -= 1;
7670        let d = *buf.add(j);
7671        if d < b'9' { *buf.add(j) = d + 1; return; }
7672        *buf.add(j) = b'0';
7673    }
7674}
7675
7676// Write float to dst. Returns bytes written. Does NOT handle width/flags padding.
7677unsafe fn format_f64_full(
7678    dst: *mut u8, val: f64, fmt_type: u8, precision: i32, flags: u8, uppercase: bool,
7679) -> usize {
7680    let mut pos = 0usize;
7681
7682    // NaN
7683    if val.is_nan() {
7684        let nan_s: &[u8] = if uppercase { b"NAN" } else { b"nan" };
7685        core::ptr::copy_nonoverlapping(nan_s.as_ptr(), dst, 3);
7686        return 3;
7687    }
7688    let neg = val.is_sign_negative();
7689    let v = if neg { -val } else { val };
7690
7691    // Inf
7692    if v.is_infinite() {
7693        if neg { *dst.add(pos) = b'-'; pos += 1; }
7694        else if flags & FLAG_PLUS != 0 { *dst.add(pos) = b'+'; pos += 1; }
7695        else if flags & FLAG_SPACE != 0 { *dst.add(pos) = b' '; pos += 1; }
7696        let inf_s: &[u8] = if uppercase { b"INF" } else { b"inf" };
7697        core::ptr::copy_nonoverlapping(inf_s.as_ptr(), dst.add(pos), 3);
7698        return pos + 3;
7699    }
7700
7701    // Sign
7702    if neg { *dst.add(pos) = b'-'; pos += 1; }
7703    else if flags & FLAG_PLUS != 0 { *dst.add(pos) = b'+'; pos += 1; }
7704    else if flags & FLAG_SPACE != 0 { *dst.add(pos) = b' '; pos += 1; }
7705
7706    // === Hex float ===
7707    if fmt_type == FMT_A {
7708        return pos + fmt_hex_float(dst.add(pos), val, precision, flags, uppercase);
7709    }
7710
7711    // === Decimal formats ===
7712    let p: usize;
7713    let mut exp: i32;
7714    let use_f: bool;
7715
7716    match fmt_type {
7717        FMT_G => {
7718            let gp = if precision < 0 { 6usize } else if precision == 0 { 1usize } else { precision as usize };
7719            exp = if v > 0.0 { compute_exp10(v) } else { 0 };
7720            use_f = exp >= -4 && exp < gp as i32;
7721            if use_f {
7722                p = if exp < 0 { gp + ((-exp) as usize - 1) }
7723                    else if (exp as usize) < gp { gp - exp as usize - 1 }
7724                    else { 0 };
7725            } else {
7726                p = gp - 1;
7727            }
7728        }
7729        FMT_E => {
7730            p = if precision < 0 { 6usize } else { precision as usize };
7731            exp = if v > 0.0 { compute_exp10(v) } else { 0 };
7732            use_f = false;
7733        }
7734        _ => {
7735            p = if precision < 0 { 6usize } else { precision as usize };
7736            exp = 0;
7737            use_f = true;
7738        }
7739    }
7740
7741    if v == 0.0 {
7742        *dst.add(pos) = b'0'; pos += 1;
7743        if !(fmt_type == FMT_G && flags & FLAG_HASH == 0) {
7744            if p > 0 || flags & FLAG_HASH != 0 {
7745                *dst.add(pos) = b'.'; pos += 1;
7746                for _ in 0..p { *dst.add(pos) = b'0'; pos += 1; }
7747            }
7748        }
7749        if !use_f {
7750            let ec = if uppercase { b'E' } else { b'e' };
7751            *dst.add(pos) = ec; pos += 1;
7752            *dst.add(pos) = b'+'; pos += 1;
7753            *dst.add(pos) = b'0'; pos += 1;
7754            *dst.add(pos) = b'0'; pos += 1;
7755        }
7756        return pos;
7757    }
7758
7759    // === %f format: exact bit-based arithmetic, returns early ===
7760    if use_f {
7761        let bits = v.to_bits();
7762        let raw_mant = bits & 0x000FFFFFFFFFFFFF;
7763        let raw_exp_bits = ((bits >> 52) & 0x7FF) as i32;
7764        let (mant_b, bin_exp) = if raw_exp_bits == 0 {
7765            (raw_mant, -1022i32)
7766        } else {
7767            (raw_mant | (1u64 << 52), raw_exp_bits - 1023)
7768        };
7769
7770        // Integer part as decimal string
7771        let mut ibuf = [0u8; 400];
7772        let mut ilen: usize;
7773        if bin_exp < 0 {
7774            ibuf[0] = b'0'; ilen = 1;
7775        } else if bin_exp <= 63 {
7776            let int_val = if bin_exp <= 52 { mant_b >> (52 - bin_exp) }
7777                          else { mant_b << (bin_exp - 52) };
7778            let (buf2, len2) = format_u64(int_val);
7779            ibuf[..len2].copy_from_slice(&buf2[..len2]);
7780            ilen = len2;
7781        } else {
7782            ilen = bigint_to_decimal(ibuf.as_mut_ptr(), mant_b, (bin_exp - 52) as u32);
7783        }
7784
7785        // Fractional digits
7786        let mut fbuf = [0u8; 1100];
7787        let mut has_rest = false;
7788        if p > 0 && bin_exp < 52 {
7789            let shift = (52 - bin_exp) as u32;
7790            let frac_bits = if bin_exp >= 0 { mant_b & ((1u64 << shift) - 1) } else { mant_b };
7791            if shift <= 124 {
7792                has_rest = frac_digits_u128(fbuf.as_mut_ptr(), frac_bits, shift, p + 1);
7793            } else {
7794                has_rest = frac_digits_bigint(fbuf.as_mut_ptr(), frac_bits, shift, p + 1);
7795            }
7796        }
7797
7798        // Rounding
7799        if p > 0 && bin_exp < 52 {
7800            let next_d = fbuf[p];
7801            let last_d = fbuf[p - 1];
7802            if next_d > 5 || (next_d == 5 && (has_rest || last_d & 1 != 0)) {
7803                let mut carry = true;
7804                for j in (0..p).rev() {
7805                    if !carry { break; }
7806                    fbuf[j] += 1;
7807                    if fbuf[j] < 10 { carry = false; break; }
7808                    fbuf[j] = 0;
7809                }
7810                if carry { increment_decimal(ibuf.as_mut_ptr(), &mut ilen); }
7811            }
7812        } else if p == 0 {
7813            // %.0f: round integer based on fractional part
7814            if bin_exp >= 0 && bin_exp < 52 {
7815                let shift = (52 - bin_exp) as u32;
7816                let frac_bits = mant_b & ((1u64 << shift) - 1);
7817                let half = 1u64 << (shift - 1);
7818                let last_d = ibuf[ilen - 1] - b'0';
7819                if frac_bits > half || (frac_bits == half && last_d & 1 != 0) {
7820                    increment_decimal(ibuf.as_mut_ptr(), &mut ilen);
7821                }
7822            }
7823        }
7824
7825        // Output
7826        core::ptr::copy_nonoverlapping(ibuf.as_ptr(), dst.add(pos), ilen);
7827        pos += ilen;
7828        if p > 0 || flags & FLAG_HASH != 0 {
7829            *dst.add(pos) = b'.'; pos += 1;
7830            for i in 0..p { *dst.add(pos) = b'0' + fbuf[i]; pos += 1; }
7831        }
7832        if fmt_type == FMT_G && flags & FLAG_HASH == 0 {
7833            while pos > 0 && *dst.add(pos - 1) == b'0' { pos -= 1; }
7834            if pos > 0 && *dst.add(pos - 1) == b'.' { pos -= 1; }
7835        }
7836        return pos;
7837    }
7838
7839    // === %e/%g format: exact big-integer digit extraction ===
7840    // D = floor(v * 10^(p+1-exp)), using v = mant_b * 2^(bin_exp-52)
7841    let mut fbuf = [0u8; 20];
7842    let mut int_part: u64;
7843    {
7844        let bits_v = v.to_bits();
7845        let raw_mant_v = bits_v & 0x000FFFFFFFFFFFFF;
7846        let raw_exp_v = ((bits_v >> 52) & 0x7FF) as i32;
7847        let (mant_b_v, bin_exp_v) = if raw_exp_v == 0 {
7848            (raw_mant_v, -1022i32)
7849        } else {
7850            (raw_mant_v | (1u64 << 52), raw_exp_v - 1023)
7851        };
7852
7853        let s: i32 = (p as i32) + 1 - exp;
7854        let bs: i32 = bin_exp_v - 52 + s;
7855
7856        let mut limbs = [0u32; 40];
7857        limbs[0] = mant_b_v as u32;
7858        limbs[1] = (mant_b_v >> 32) as u32;
7859        let mut nlimbs: usize = if limbs[1] != 0 { 2 } else { 1 };
7860
7861        if s > 0 {
7862            for _ in 0..(s as usize) {
7863                let mut carry = 0u64;
7864                for j in 0..nlimbs {
7865                    let val = limbs[j] as u64 * 5 + carry;
7866                    limbs[j] = val as u32;
7867                    carry = val >> 32;
7868                }
7869                if carry > 0 { limbs[nlimbs] = carry as u32; nlimbs += 1; }
7870            }
7871        }
7872
7873        if bs > 0 {
7874            let ws = (bs as u32 / 32) as usize;
7875            let bit = bs as u32 % 32;
7876            if ws > 0 {
7877                let mut i = nlimbs;
7878                while i > 0 { i -= 1; if i + ws < 40 { limbs[i + ws] = limbs[i]; } limbs[i] = 0; }
7879                nlimbs += ws;
7880            }
7881            if bit > 0 {
7882                let mut carry = 0u32;
7883                for i in ws..nlimbs {
7884                    let new_val = (limbs[i] << bit) | carry;
7885                    carry = limbs[i] >> (32 - bit);
7886                    limbs[i] = new_val;
7887                }
7888                if carry > 0 && nlimbs < 40 { limbs[nlimbs] = carry; nlimbs += 1; }
7889            }
7890        }
7891
7892        let mut has_rest = false;
7893        if s < 0 {
7894            for _ in 0..(-s as usize) {
7895                let mut rem = 0u64;
7896                for i in (0..nlimbs).rev() {
7897                    let val = (rem << 32) | limbs[i] as u64;
7898                    limbs[i] = (val / 5) as u32;
7899                    rem = val % 5;
7900                }
7901                if rem != 0 { has_rest = true; }
7902            }
7903        }
7904
7905        if bs < 0 {
7906            let rshift = (-bs) as u32;
7907            let ws = (rshift / 32) as usize;
7908            let bit = rshift % 32;
7909            if ws > 0 {
7910                for j in 0..ws.min(nlimbs) { if limbs[j] != 0 { has_rest = true; } }
7911                for i in ws..nlimbs { limbs[i - ws] = limbs[i]; }
7912                nlimbs = nlimbs.saturating_sub(ws);
7913            }
7914            if bit > 0 && nlimbs > 0 {
7915                let mask = (1u32 << bit) - 1;
7916                if limbs[0] & mask != 0 { has_rest = true; }
7917                for i in 0..nlimbs - 1 {
7918                    limbs[i] = (limbs[i] >> bit) | (limbs[i + 1] << (32 - bit));
7919                }
7920                limbs[nlimbs - 1] >>= bit;
7921            }
7922        }
7923
7924        while nlimbs > 0 && limbs[nlimbs - 1] == 0 { nlimbs -= 1; }
7925
7926        let ndigits = p + 2;
7927        let mut all_digits = [0u8; 25];
7928        let mut nd = 0usize;
7929        if nlimbs > 0 {
7930            loop {
7931                let mut all_zero = true;
7932                for i in 0..nlimbs { if limbs[i] != 0 { all_zero = false; break; } }
7933                if all_zero { break; }
7934                let mut rem64 = 0u64;
7935                for i in (0..nlimbs).rev() {
7936                    let val = (rem64 << 32) | limbs[i] as u64;
7937                    limbs[i] = (val / 10) as u32;
7938                    rem64 = val % 10;
7939                }
7940                if nd < 25 { all_digits[nd] = rem64 as u8; }
7941                nd += 1;
7942            }
7943        }
7944
7945        if nd >= ndigits {
7946            for i in 0..ndigits { fbuf[ndigits - 1 - i] = all_digits[i]; }
7947        } else {
7948            for i in 0..nd { fbuf[ndigits - 1 - i] = all_digits[i]; }
7949            for i in nd..ndigits { fbuf[ndigits - 1 - i] = 0; }
7950        }
7951
7952        int_part = fbuf[0] as u64;
7953
7954        // Round half to even: next_d is the rounding digit beyond precision
7955        if p > 0 {
7956            let next_d = fbuf[p + 1];
7957            let last_d = fbuf[p];
7958            if next_d > 5 || (next_d == 5 && (has_rest || last_d & 1 != 0)) {
7959                let mut carry = true;
7960                for j in (1..=p).rev() {
7961                    if !carry { break; }
7962                    let d = fbuf[j] + 1;
7963                    if d < 10 { fbuf[j] = d; carry = false; break; }
7964                    fbuf[j] = 0;
7965                }
7966                if carry { int_part += 1; }
7967            }
7968        } else {
7969            let next_d = fbuf[1];
7970            if next_d > 5 || (next_d == 5 && (has_rest || int_part & 1 != 0)) {
7971                int_part += 1;
7972            }
7973        }
7974
7975        // fbuf[1..=p] are now the fractional digits for output
7976        // Shift them to fbuf[0..p] so output code can index fbuf[0..p]
7977        for i in 0..p { fbuf[i] = fbuf[i + 1]; }
7978    }
7979
7980    if !use_f && int_part >= 10 {
7981        exp += 1;
7982        int_part = 1;
7983    }
7984
7985    // Format output
7986    if use_f {
7987        // %f style
7988        let (ibuf, ilen) = format_u64(int_part);
7989        core::ptr::copy_nonoverlapping(ibuf.as_ptr(), dst.add(pos), ilen);
7990        pos += ilen;
7991        if p > 0 || flags & FLAG_HASH != 0 {
7992            *dst.add(pos) = b'.'; pos += 1;
7993            for idx in 0..p { *dst.add(pos) = b'0' + fbuf[idx]; pos += 1; }
7994        }
7995        if fmt_type == FMT_G && flags & FLAG_HASH == 0 {
7996            while pos > 0 && *dst.add(pos - 1) == b'0' { pos -= 1; }
7997            if pos > 0 && *dst.add(pos - 1) == b'.' { pos -= 1; }
7998        }
7999    } else {
8000        // %e style
8001        *dst.add(pos) = b'0' + int_part as u8; pos += 1;
8002        if p > 0 || flags & FLAG_HASH != 0 {
8003            *dst.add(pos) = b'.'; pos += 1;
8004            for idx in 0..p { *dst.add(pos) = b'0' + fbuf[idx]; pos += 1; }
8005        }
8006        if fmt_type == FMT_G && flags & FLAG_HASH == 0 {
8007            while pos > 0 && *dst.add(pos - 1) == b'0' { pos -= 1; }
8008            if pos > 0 && *dst.add(pos - 1) == b'.' { pos -= 1; }
8009        }
8010        let ec = if uppercase { b'E' } else { b'e' };
8011        *dst.add(pos) = ec; pos += 1;
8012        *dst.add(pos) = if exp < 0 { b'-' } else { b'+' }; pos += 1;
8013        let eabs = if exp < 0 { (-exp) as u32 } else { exp as u32 };
8014        if eabs < 10 { *dst.add(pos) = b'0'; pos += 1; }
8015        let (ebuf, elen) = format_u64(eabs as u64);
8016        core::ptr::copy_nonoverlapping(ebuf.as_ptr(), dst.add(pos), elen);
8017        pos += elen;
8018    }
8019
8020    pos
8021}
8022
8023// Compute decimal exponent: floor(log10(v)) with correction
8024unsafe fn compute_exp10(v: f64) -> i32 {
8025    let e = libm::floor(libm::log10(v)) as i32;
8026    let probe = v / libm::pow(10.0, e as f64);
8027    if probe >= 10.0 { e + 1 }
8028    else if probe < 1.0 { e - 1 }
8029    else { e }
8030}
8031
8032// Write hex float to dst. Returns bytes written.
8033unsafe fn fmt_hex_float(dst: *mut u8, val: f64, precision: i32, _flags: u8, uppercase: bool) -> usize {
8034    // Handle zero (and negative zero) specially
8035    if val.to_bits() & 0x7FFFFFFFFFFFFFFF == 0 {
8036        let lc = !uppercase;
8037        let pfx: &[u8] = if lc { b"0x" } else { b"0X" };
8038        let p = if precision < 0 { 0usize } else { precision as usize };
8039        let mut pos = 0usize;
8040        core::ptr::copy_nonoverlapping(pfx.as_ptr(), dst.add(pos), 2); pos += 2;
8041        *dst.add(pos) = b'0'; pos += 1;
8042        if p > 0 {
8043            *dst.add(pos) = b'.'; pos += 1;
8044            for _ in 0..p { *dst.add(pos) = b'0'; pos += 1; }
8045        }
8046        let pc = if lc { b'p' } else { b'P' };
8047        *dst.add(pos) = pc; pos += 1;
8048        *dst.add(pos) = b'+'; pos += 1;
8049        *dst.add(pos) = b'0'; pos += 1;
8050        return pos;
8051    }
8052    let bits = val.to_bits();
8053    let exp_bits = ((bits >> 52) & 0x7FF) as i32;
8054    let mant_bits = bits & 0x000FFFFFFFFFFFFF;
8055    let (mut e, mut m) = if exp_bits == 0 { (1 - 1023, mant_bits) } else { (exp_bits - 1023, mant_bits | (1u64 << 52)) };
8056    // musl normalizes subnormals so the leading digit is always 1, which
8057    // lets the exponent go below -1022 (e.g. 0x1p-1074 for the smallest).
8058    if exp_bits == 0 && m != 0 {
8059        let shift = m.leading_zeros() as i32 - 11;
8060        m <<= shift;
8061        e -= shift;
8062    }
8063    let explicit = precision >= 0;
8064    let p = if explicit { precision as usize } else { 13usize };
8065    let mut pos = 0usize;
8066    let lc = !uppercase;
8067    let pfx: &[u8] = if lc { b"0x" } else { b"0X" };
8068    core::ptr::copy_nonoverlapping(pfx.as_ptr(), dst.add(pos), 2); pos += 2;
8069
8070    // Leading digit from bit 52 (1 for every normalized value).
8071    let lead_pos = pos;
8072    let mut lead = ((m >> 52) & 0xF) as u8;
8073    *dst.add(pos) = hex_digit_char(lead, lc); pos += 1;
8074    let mut frac_start = pos;
8075    if p > 0 {
8076        *dst.add(pos) = b'.'; pos += 1;
8077        frac_start = pos;
8078    }
8079
8080    // Extract p hex digits from the fraction, most significant nibble first.
8081    let mut rm = m;
8082    for _ in 0..p {
8083        let nib = ((rm >> 48) & 0xF) as u8;
8084        *dst.add(pos) = hex_digit_char(nib, lc); pos += 1;
8085        rm <<= 4;
8086    }
8087
8088    // Round to nearest, ties to even, based on the next nibble.
8089    let round_nib = ((rm >> 48) & 0xF) as u8;
8090    let rest = rm & 0x0000FFFFFFFFFFFF;
8091    let last_nib = if p > 0 {
8092        hex_char_val(*dst.add(pos - 1))
8093    } else {
8094        lead
8095    };
8096    let should_round = round_nib > 8 || (round_nib == 8 && (rest != 0 || (last_nib & 1) != 0));
8097    if should_round {
8098        let mut j = pos;
8099        loop {
8100            if j == frac_start {
8101                // Carry out of the fraction into the leading digit.
8102                lead += 1;
8103                *dst.add(lead_pos) = hex_digit_char(lead, lc);
8104                break;
8105            }
8106            j -= 1;
8107            let v = hex_char_val(*dst.add(j));
8108            if v < 15 {
8109                *dst.add(j) = hex_digit_char(v + 1, lc);
8110                break;
8111            }
8112            *dst.add(j) = b'0';
8113        }
8114    }
8115
8116    // With no explicit precision, print the shortest exact representation:
8117    // trim trailing fractional zeros and drop the radix point if empty.
8118    if !explicit {
8119        while pos > frac_start && *dst.add(pos - 1) == b'0' {
8120            pos -= 1;
8121        }
8122        if pos == frac_start {
8123            pos = frac_start - 1;
8124        }
8125    }
8126
8127    // Exponent
8128    let pc = if lc { b'p' } else { b'P' };
8129    *dst.add(pos) = pc; pos += 1;
8130    *dst.add(pos) = if e < 0 { b'-' } else { b'+' }; pos += 1;
8131    let eabs = if e < 0 { (-e) as u32 } else { e as u32 };
8132    let (ebuf, elen) = format_u64(eabs as u64);
8133    core::ptr::copy_nonoverlapping(ebuf.as_ptr(), dst.add(pos), elen);
8134    pos += elen;
8135    pos
8136}
8137
8138#[inline]
8139fn hex_digit_char(v: u8, lowercase: bool) -> u8 {
8140    if v < 10 { b'0' + v }
8141    else if lowercase { b'a' + v - 10 }
8142    else { b'A' + v - 10 }
8143}
8144
8145#[inline]
8146fn hex_char_val(c: u8) -> u8 {
8147    if c >= b'0' && c <= b'9' { c - b'0' }
8148    else if c >= b'a' && c <= b'f' { c - b'a' + 10 }
8149    else if c >= b'A' && c <= b'F' { c - b'A' + 10 }
8150    else { 0 }
8151}
8152
8153// Apply width and flags padding to a formatted string in buf[0..len].
8154// Writes the final result to dst. Returns total bytes written.
8155unsafe fn apply_width_flags(
8156    dst: *mut u8, buf: *const u8, len: usize, width: usize, flags: u8,
8157) -> usize {
8158    if len >= width {
8159        core::ptr::copy_nonoverlapping(buf, dst, len);
8160        return len;
8161    }
8162    let pad = width - len;
8163    let pad_char = if flags & FLAG_ZERO != 0 && flags & FLAG_MINUS == 0 { b'0' } else { b' ' };
8164    if flags & FLAG_MINUS != 0 {
8165        // Left align
8166        core::ptr::copy_nonoverlapping(buf, dst, len);
8167        for i in 0..pad { *dst.add(len + i) = pad_char; }
8168    } else {
8169        // Right align
8170        // For zero-padded numbers, sign goes before zeros
8171        if flags & FLAG_ZERO != 0 && len > 0 {
8172            let first = *buf;
8173            if first == b'-' || first == b'+' || first == b' ' {
8174                *dst = first;
8175                for i in 0..pad { *dst.add(1 + i) = pad_char; }
8176                core::ptr::copy_nonoverlapping(buf.add(1), dst.add(1 + pad), len - 1);
8177                return width;
8178            }
8179        }
8180        for i in 0..pad { *dst.add(i) = pad_char; }
8181        core::ptr::copy_nonoverlapping(buf, dst.add(pad), len);
8182    }
8183    width
8184}
8185
8186#[no_mangle]
8187pub unsafe extern "C" fn vprintf(fmt: *const c_char, mut args: VaList) -> c_int {
8188    impl_format!(fmt, args,
8189        |c: u8| { let _ = fwrite(&c as *const u8 as *const c_void, 1, 1, stdout); },
8190        |s: *const u8, len: usize| { let _ = fwrite(s as *const c_void, 1, len, stdout); }
8191    )
8192}
8193
8194#[no_mangle]
8195pub unsafe extern "C" fn vfprintf(stream: *mut FILE, fmt: *const c_char, mut args: VaList) -> c_int {
8196    impl_format!(fmt, args,
8197        |c: u8| { let _ = fwrite(&c as *const u8 as *const c_void, 1, 1, stream); },
8198        |s: *const u8, len: usize| { let _ = fwrite(s as *const c_void, 1, len, stream); }
8199    )
8200}
8201
8202#[no_mangle]
8203pub unsafe extern "C" fn printf(fmt: *const c_char, mut args: ...) -> c_int {
8204    impl_format!(fmt, args,
8205        |c: u8| { let _ = fwrite(&c as *const u8 as *const c_void, 1, 1, stdout); },
8206        |s: *const u8, len: usize| { let _ = fwrite(s as *const c_void, 1, len, stdout); }
8207    )
8208}
8209
8210#[no_mangle]
8211pub unsafe extern "C" fn fprintf(stream: *mut FILE, fmt: *const c_char, mut args: ...) -> c_int {
8212    impl_format!(fmt, args,
8213        |c: u8| { let _ = fwrite(&c as *const u8 as *const c_void, 1, 1, stream); },
8214        |s: *const u8, len: usize| { let _ = fwrite(s as *const c_void, 1, len, stream); }
8215    )
8216}
8217
8218#[no_mangle]
8219unsafe fn format_write_fd(fd: c_int, fmt: *const c_char, args: &mut VaList<'_>) -> c_int {
8220    // cgroupfs (and some other pseudo-filesystems) parse one command per
8221    // write() call, so the whole formatted string must go out at once.
8222    let mut stack = [0u8; 4096];
8223    let mut a1 = args.clone();
8224    let n = format_to_buf(stack.as_mut_ptr(), stack.len(), fmt, &mut a1);
8225    if n < 0 { return -1; }
8226    if (n as usize) < stack.len() {
8227        let w = crate::write(fd, stack.as_ptr() as *const c_void, n as usize);
8228        if w < 0 { return -1; }
8229        return n;
8230    }
8231    let heap = crate::malloc(n as usize + 1) as *mut u8;
8232    if heap.is_null() { return -1; }
8233    let mut a2 = args.clone();
8234    let n2 = format_to_buf(heap, n as usize + 1, fmt, &mut a2);
8235    if n2 < 0 { crate::free(heap as *mut c_void); return -1; }
8236    let w = crate::write(fd, heap as *const c_void, n2 as usize);
8237    crate::free(heap as *mut c_void);
8238    if w < 0 { return -1; }
8239    n2
8240}
8241
8242#[no_mangle]
8243pub unsafe extern "C" fn dprintf(fd: c_int, fmt: *const c_char, mut args: ...) -> c_int {
8244    format_write_fd(fd, fmt, &mut args)
8245}
8246
8247#[no_mangle]
8248pub unsafe extern "C" fn vdprintf(fd: c_int, fmt: *const c_char, mut args: VaList) -> c_int {
8249    format_write_fd(fd, fmt, &mut args)
8250}
8251
8252// ============================================================
8253// sprintf / snprintf / vsprintf / vsnprintf
8254// ============================================================
8255
8256unsafe fn format_to_buf(buf: *mut u8, cap: usize, fmt: *const c_char, args: &mut VaList<'_>) -> c_int {
8257    let mut pos = 0usize;
8258    let mut i = 0usize;
8259    let mut count = 0usize;
8260    macro_rules! wc {
8261        ($c:expr) => { if pos < cap { *buf.add(pos) = $c; } pos += 1; count += 1; }
8262    }
8263    macro_rules! ws {
8264        ($s:expr, $len:expr) => {{
8265            let __len = $len;
8266            for k in 0..__len { if pos + k < cap { *buf.add(pos + k) = *$s.add(k); } }
8267            pos += __len; count += __len;
8268        }}
8269    }
8270    // Write formatted buffer respecting cap, at current pos
8271    macro_rules! ws_buf {
8272        ($src:expr, $len:expr) => {{
8273            let __len = $len;
8274            for k in 0..__len { if pos + k < cap { *buf.add(pos + k) = *$src.add(k); } }
8275            pos += __len; count += __len;
8276        }}
8277    }
8278    loop {
8279        let c = *fmt.add(i) as u8;
8280        if c == 0 { break; }
8281        if c != b'%' { wc!(c); i += 1; continue; }
8282        i += 1;
8283        // Parse flags
8284        let mut flags: u8 = 0;
8285        loop {
8286            let fc = *fmt.add(i) as u8;
8287            match fc {
8288                b'-' => { flags |= FLAG_MINUS; i += 1; }
8289                b'+' => { flags |= FLAG_PLUS; i += 1; }
8290                b' ' => { flags |= FLAG_SPACE; i += 1; }
8291                b'0' => { flags |= FLAG_ZERO; i += 1; }
8292                b'#' => { flags |= FLAG_HASH; i += 1; }
8293                _ => break,
8294            }
8295        }
8296        // Parse width
8297        let mut width: usize = 0;
8298        if *fmt.add(i) as u8 == b'*' {
8299            width = args.next_arg::<c_int>() as usize; i += 1;
8300        } else {
8301            while (*fmt.add(i) as u8) >= b'0' && (*fmt.add(i) as u8) <= b'9' {
8302                width = width * 10 + ((*fmt.add(i) as u8) - b'0') as usize; i += 1;
8303            }
8304        }
8305        // Parse precision
8306        let mut precision: i32 = -1;
8307        if *fmt.add(i) as u8 == b'.' {
8308            i += 1;
8309            if *fmt.add(i) as u8 == b'*' {
8310                precision = args.next_arg::<c_int>(); i += 1;
8311            } else {
8312                precision = 0;
8313                while (*fmt.add(i) as u8) >= b'0' && (*fmt.add(i) as u8) <= b'9' {
8314                    precision = precision * 10 + ((*fmt.add(i) as u8) - b'0') as i32; i += 1;
8315                }
8316            }
8317        }
8318        // Parse length modifier
8319        let len_mod = *fmt.add(i) as u8;
8320        let spec: u8;
8321        if len_mod == b'h' || len_mod == b'l' || len_mod == b'j' || len_mod == b'z' || len_mod == b't' || len_mod == b'L' {
8322            i += 1;
8323            let len_mod2 = *fmt.add(i) as u8;
8324            if (len_mod == b'h' && len_mod2 == b'h') || (len_mod == b'l' && len_mod2 == b'l') {
8325                i += 1;
8326                spec = *fmt.add(i) as u8;
8327                match (len_mod, len_mod2, spec) {
8328                    (b'h', b'h', b'n') => {
8329                        let p = args.next_arg::<*mut c_void>();
8330                        if !p.is_null() { *(p as *mut u8) = count as u8; }
8331                    }
8332                    (b'l', b'l', b'n') => {
8333                        let p = args.next_arg::<*mut c_void>();
8334                        if !p.is_null() { *(p as *mut c_longlong) = count as c_longlong; }
8335                    }
8336                    (b'l', b'l', b'd') | (b'l', b'l', b'i') => {
8337                        let val = args.next_arg::<c_longlong>();
8338                        let neg = val < 0;
8339                        let abs = if neg { val.wrapping_neg() as u64 } else { val as u64 };
8340                        let b = format_u64(abs);
8341                        let sign = if neg { Some(b'-') } else if flags & FLAG_PLUS != 0 { Some(b'+') } else if flags & FLAG_SPACE != 0 { Some(b' ') } else { None };
8342                        let mut fbuf = [0u8; 32];
8343                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, sign, None, precision, width, flags, false);
8344                        ws_buf!(fbuf.as_ptr(), len);
8345                    }
8346                    (b'l', b'l', b'u') => {
8347                        let val = args.next_arg::<c_ulonglong>();
8348                        let b = format_u64(val as u64);
8349                        let mut fbuf = [0u8; 32];
8350                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, None, precision, width, flags, false);
8351                        ws_buf!(fbuf.as_ptr(), len);
8352                    }
8353                    (b'l', b'l', b'o') => {
8354                        let val = args.next_arg::<c_ulonglong>();
8355                        let b = format_octal(val as u64, flags & FLAG_HASH != 0);
8356                        let mut fbuf = [0u8; 32];
8357                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, None, precision, width, flags, flags & FLAG_HASH != 0);
8358                        ws_buf!(fbuf.as_ptr(), len);
8359                    }
8360                    (b'l', b'l', b'x') | (b'l', b'l', b'X') => {
8361                        let val = args.next_arg::<c_ulonglong>();
8362                        let b = format_hex(val as u64, spec == b'X');
8363                        let pc = if spec == b'X' { b'X' } else { b'x' };
8364                        let prefix = if flags & FLAG_HASH != 0 && !(b.1 == 1 && b.0[0] == b'0') { Some(pc) } else { None };
8365                        let mut fbuf = [0u8; 32];
8366                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, prefix, precision, width, flags, false);
8367                        ws_buf!(fbuf.as_ptr(), len);
8368                    }
8369                    _ => { wc!(b'%'); wc!(len_mod); wc!(len_mod2); wc!(spec); }
8370                }
8371            } else {
8372                spec = len_mod2;
8373                let lm = if (len_mod == b'z' || len_mod == b't') && spec != b'n' { b'l' } else { len_mod };
8374                match (lm, spec) {
8375                    (b'h', b'n') => {
8376                        let p = args.next_arg::<*mut c_void>();
8377                        if !p.is_null() { *(p as *mut u16) = count as u16; }
8378                    }
8379                    (b'l', b'n') => {
8380                        let p = args.next_arg::<*mut c_void>();
8381                        if !p.is_null() { *(p as *mut c_long) = count as c_long; }
8382                    }
8383                    (b'z', b'n') | (b't', b'n') => {
8384                        let p = args.next_arg::<*mut c_void>();
8385                        if !p.is_null() { *(p as *mut usize) = count; }
8386                    }
8387                    (b'j', b'n') => {
8388                        let p = args.next_arg::<*mut c_void>();
8389                        if !p.is_null() { *(p as *mut c_ulonglong) = count as c_ulonglong; }
8390                    }
8391                    (b'l', b'd') | (b'l', b'i') => {
8392                        let val = args.next_arg::<c_long>();
8393                        let neg = val < 0;
8394                        let abs = if neg { val.wrapping_neg() as u64 } else { val as u64 };
8395                        let b = format_u64(abs);
8396                        let sign = if neg { Some(b'-') } else if flags & FLAG_PLUS != 0 { Some(b'+') } else if flags & FLAG_SPACE != 0 { Some(b' ') } else { None };
8397                        let mut fbuf = [0u8; 32];
8398                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, sign, None, precision, width, flags, false);
8399                        ws_buf!(fbuf.as_ptr(), len);
8400                    }
8401                    (b'l', b'u') => {
8402                        let val = args.next_arg::<c_ulong>();
8403                        let b = format_u64(val as u64);
8404                        let mut fbuf = [0u8; 32];
8405                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, None, precision, width, flags, false);
8406                        ws_buf!(fbuf.as_ptr(), len);
8407                    }
8408                    (b'l', b's') => {
8409                        let p = args.next_arg::<*const wchar_t>();
8410                        if p.is_null() {
8411                            let slen = if precision >= 0 { (precision as usize).min(6) } else { 6 };
8412                            let pad = width.saturating_sub(slen);
8413                            if flags & FLAG_MINUS != 0 { ws!(b"(null)".as_ptr(), slen); for _ in 0..pad { wc!(b' '); } }
8414                            else { for _ in 0..pad { wc!(b' '); } ws!(b"(null)".as_ptr(), slen); }
8415                        } else {
8416                            let mut mb = [0u8; 4096];
8417                            let mut n = wide_to_mb(mb.as_mut_ptr(), mb.len(), p);
8418                            if precision >= 0 && (precision as usize) < n { n = precision as usize; }
8419                            let pad = width.saturating_sub(n);
8420                            if flags & FLAG_MINUS != 0 { ws!(mb.as_ptr(), n); for _ in 0..pad { wc!(b' '); } }
8421                            else { for _ in 0..pad { wc!(b' '); } ws!(mb.as_ptr(), n); }
8422                        }
8423                    }
8424                    (b'l', b'c') => {
8425                        let ch = args.next_arg::<c_int>();
8426                        let mut mb = [0u8; 8];
8427                        let mut st: c_uint = 0;
8428                        let n = wcrtomb(mb.as_mut_ptr() as *mut c_char, ch, &mut st);
8429                        if n != !0usize { ws!(mb.as_ptr(), n); }
8430                    }
8431                    (b'l', b'x') => {
8432                        let val = args.next_arg::<c_ulong>();
8433                        let b = format_hex(val as u64, false);
8434                        let prefix = if flags & FLAG_HASH != 0 && !(b.1 == 1 && b.0[0] == b'0') { Some(b'x') } else { None };
8435                        let mut fbuf = [0u8; 32];
8436                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, prefix, precision, width, flags, false);
8437                        ws_buf!(fbuf.as_ptr(), len);
8438                    }
8439                    (b'l', b'X') => {
8440                        let val = args.next_arg::<c_ulong>();
8441                        let b = format_hex(val as u64, true);
8442                        let prefix = if flags & FLAG_HASH != 0 && !(b.1 == 1 && b.0[0] == b'0') { Some(b'X') } else { None };
8443                        let mut fbuf = [0u8; 32];
8444                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, prefix, precision, width, flags, false);
8445                        ws_buf!(fbuf.as_ptr(), len);
8446                    }
8447                    (b'l', b'o') => {
8448                        let val = args.next_arg::<c_ulong>();
8449                        let b = format_octal(val as u64, flags & FLAG_HASH != 0);
8450                        let mut fbuf = [0u8; 32];
8451                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, None, precision, width, flags, flags & FLAG_HASH != 0);
8452                        ws_buf!(fbuf.as_ptr(), len);
8453                    }
8454                    (b'l', b'f') | (b'l', b'F') | (b'l', b'e') | (b'l', b'E')
8455                    | (b'l', b'g') | (b'l', b'G') | (b'l', b'a') | (b'l', b'A') => {
8456                        let val = args.next_arg::<f64>();
8457                        let ucase = spec == b'F' || spec == b'E' || spec == b'G' || spec == b'A';
8458                        let ftype = match spec | 0x20 { b'f' => FMT_F, b'e' => FMT_E, b'g' => FMT_G, b'a' => FMT_A, _ => FMT_F };
8459                        let mut fbuf = [0u8; 4224];
8460                        let flen = format_f64_full(fbuf.as_mut_ptr(), val, ftype, precision, flags, ucase);
8461                        let mut wbuf = [0u8; 4224];
8462                        let wlen = apply_width_flags(wbuf.as_mut_ptr(), fbuf.as_ptr(), flen, width, flags);
8463                        ws_buf!(wbuf.as_ptr(), wlen);
8464                    }
8465                    (b'L', b'f') | (b'L', b'F') | (b'L', b'e') | (b'L', b'E')
8466                    | (b'L', b'g') | (b'L', b'G') | (b'L', b'a') | (b'L', b'A') => {
8467                        #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
8468                        let val = {
8469                            let lo: u64 = args.next_arg::<u64>();
8470                            let hi: u64 = args.next_arg::<u64>();
8471                            let combined: u128 = ((hi as u128) << 64) | (lo as u128);
8472                            f128::from_bits(combined) as f64
8473                        };
8474                        #[cfg(target_arch = "x86_64")]
8475                        let val = args.next_arg::<f64>();
8476                        let ucase = spec == b'F' || spec == b'E' || spec == b'G' || spec == b'A';
8477                        let ftype = match spec | 0x20 { b'f' => FMT_F, b'e' => FMT_E, b'g' => FMT_G, b'a' => FMT_A, _ => FMT_F };
8478                        let mut fbuf = [0u8; 4224];
8479                        let flen = format_f64_full(fbuf.as_mut_ptr(), val, ftype, precision, flags, ucase);
8480                        let mut wbuf = [0u8; 4224];
8481                        let wlen = apply_width_flags(wbuf.as_mut_ptr(), fbuf.as_ptr(), flen, width, flags);
8482                        ws_buf!(wbuf.as_ptr(), wlen);
8483                    }
8484                    _ => { wc!(b'%'); wc!(len_mod); wc!(spec); }
8485                }
8486            }
8487        } else {
8488            spec = *fmt.add(i) as u8;
8489            match spec {
8490                b's' => {
8491                    let s = args.next_arg::<*const c_char>();
8492                    if s.is_null() {
8493                        let slen = if precision >= 0 { (precision as usize).min(6) } else { 6 };
8494                        let padded_len = if width > slen { width } else { slen };
8495                        let pad = padded_len - slen;
8496                        if flags & FLAG_MINUS != 0 {
8497                            ws!(b"(null)".as_ptr(), slen);
8498                            for _ in 0..pad { wc!(b' '); }
8499                        } else {
8500                            for _ in 0..pad { wc!(b' '); }
8501                            ws!(b"(null)".as_ptr(), slen);
8502                        }
8503                    } else {
8504                        let full_len = strlen(s as *const c_char);
8505                        let slen = if precision >= 0 { (precision as usize).min(full_len) } else { full_len };
8506                        let padded_len = if width > slen { width } else { slen };
8507                        let pad = padded_len - slen;
8508                        if flags & FLAG_MINUS != 0 {
8509                            ws!(s as *const u8, slen);
8510                            for _ in 0..pad { wc!(b' '); }
8511                        } else {
8512                            for _ in 0..pad { wc!(b' '); }
8513                            ws!(s as *const u8, slen);
8514                        }
8515                    }
8516                }
8517                b'd' | b'i' => {
8518                    let d = args.next_arg::<c_int>();
8519                    let neg = d < 0;
8520                    let abs = if neg { d.wrapping_neg() as u64 } else { d as u64 };
8521                    let b = format_u64(abs);
8522                    let sign = if neg { Some(b'-') } else if flags & FLAG_PLUS != 0 { Some(b'+') } else if flags & FLAG_SPACE != 0 { Some(b' ') } else { None };
8523                    let mut fbuf = [0u8; 32];
8524                    let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, sign, None, precision, width, flags, false);
8525                    ws_buf!(fbuf.as_ptr(), len);
8526                }
8527                b'u' => {
8528                    let u = args.next_arg::<c_uint>();
8529                    let b = format_u64(u as u64);
8530                    let mut fbuf = [0u8; 32];
8531                    let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, None, precision, width, flags, false);
8532                    ws_buf!(fbuf.as_ptr(), len);
8533                }
8534                b'x' => {
8535                    let x = args.next_arg::<c_uint>();
8536                    let b = format_hex(x as u64, false);
8537                    let prefix = if flags & FLAG_HASH != 0 && !(b.1 == 1 && b.0[0] == b'0') { Some(b'x') } else { None };
8538                    let mut fbuf = [0u8; 32];
8539                    let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, prefix, precision, width, flags, false);
8540                    ws_buf!(fbuf.as_ptr(), len);
8541                }
8542                b'X' => {
8543                    let x = args.next_arg::<c_uint>();
8544                    let b = format_hex(x as u64, true);
8545                    let prefix = if flags & FLAG_HASH != 0 && !(b.1 == 1 && b.0[0] == b'0') { Some(b'X') } else { None };
8546                    let mut fbuf = [0u8; 32];
8547                    let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, prefix, precision, width, flags, false);
8548                    ws_buf!(fbuf.as_ptr(), len);
8549                }
8550                b'o' => {
8551                    let o = args.next_arg::<c_uint>();
8552                    let b = format_octal(o as u64, flags & FLAG_HASH != 0);
8553                    let mut fbuf = [0u8; 32];
8554                    let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, None, precision, width, flags, flags & FLAG_HASH != 0);
8555                    ws_buf!(fbuf.as_ptr(), len);
8556                }
8557                b'c' => {
8558                    let ch = args.next_arg::<c_int>();
8559                    if width > 1 {
8560                        let pad = width - 1;
8561                        if flags & FLAG_MINUS != 0 {
8562                            wc!(ch as u8);
8563                            for _ in 0..pad { wc!(b' '); }
8564                        } else {
8565                            for _ in 0..pad { wc!(b' '); }
8566                            wc!(ch as u8);
8567                        }
8568                    } else {
8569                        wc!(ch as u8);
8570                    }
8571                }
8572                b'p' => {
8573                    wc!(b'0'); wc!(b'x');
8574                    let p = args.next_arg::<*const c_void>();
8575                    let b = format_hex(p as u64, false);
8576                    ws!(b.0.as_ptr(), b.1);
8577                }
8578                b'f' | b'F' | b'e' | b'E' | b'g' | b'G' | b'a' | b'A' => {
8579                    let val = args.next_arg::<f64>();
8580                    let ucase = spec == b'F' || spec == b'E' || spec == b'G' || spec == b'A';
8581                    let ftype = match spec | 0x20 { b'f' => FMT_F, b'e' => FMT_E, b'g' => FMT_G, b'a' => FMT_A, _ => FMT_F };
8582                    let mut fbuf = [0u8; 4224];
8583                    let flen = format_f64_full(fbuf.as_mut_ptr(), val, ftype, precision, flags, ucase);
8584                    let mut wbuf = [0u8; 4224];
8585                    let wlen = apply_width_flags(wbuf.as_mut_ptr(), fbuf.as_ptr(), flen, width, flags);
8586                    ws_buf!(wbuf.as_ptr(), wlen);
8587                }
8588                b'%' => { wc!(b'%'); }
8589                b'n' => {
8590                    let p = args.next_arg::<*mut c_void>();
8591                    if !p.is_null() { *(p as *mut c_int) = count as c_int; }
8592                }
8593                _ => { wc!(b'%'); wc!(spec); }
8594            }
8595        }
8596        i += 1;
8597    }
8598    if cap > 0 { let null_pos = if pos < cap { pos } else { cap - 1 }; *buf.add(null_pos) = 0; }
8599    count as c_int
8600}
8601
8602#[no_mangle]
8603pub unsafe extern "C" fn vsprintf(buf: *mut c_char, fmt: *const c_char, mut args: VaList) -> c_int {
8604    format_to_buf(buf as *mut u8, usize::MAX, fmt, &mut args)
8605}
8606
8607#[no_mangle]
8608pub unsafe extern "C" fn vsnprintf(buf: *mut c_char, size: usize, fmt: *const c_char, mut args: VaList) -> c_int {
8609    format_to_buf(buf as *mut u8, size, fmt, &mut args)
8610}
8611
8612#[no_mangle]
8613pub unsafe extern "C" fn sprintf(buf: *mut c_char, fmt: *const c_char, mut args: ...) -> c_int {
8614    format_to_buf(buf as *mut u8, usize::MAX, fmt, &mut args)
8615}
8616
8617#[no_mangle]
8618pub unsafe extern "C" fn snprintf(buf: *mut c_char, size: usize, fmt: *const c_char, mut args: ...) -> c_int {
8619    format_to_buf(buf as *mut u8, size, fmt, &mut args)
8620}
8621
8622#[no_mangle]
8623pub unsafe extern "C" fn vasprintf(strp: *mut *mut c_char, fmt: *const c_char, ap: VaList) -> c_int {
8624    let len = vsnprintf(core::ptr::null_mut(), 0, fmt, ap.clone());
8625    if len < 0 {
8626        *strp = core::ptr::null_mut();
8627        return -1;
8628    }
8629    let p = malloc((len as usize) + 1) as *mut c_char;
8630    if p.is_null() {
8631        *strp = core::ptr::null_mut();
8632        return -1;
8633    }
8634    let ret = vsnprintf(p, (len as usize) + 1, fmt, ap.clone());
8635    if ret < 0 {
8636        free(p as *mut c_void);
8637        *strp = core::ptr::null_mut();
8638        return -1;
8639    }
8640    *strp = p;
8641    ret
8642}
8643
8644#[no_mangle]
8645pub unsafe extern "C" fn asprintf(strp: *mut *mut c_char, fmt: *const c_char, args: ...) -> c_int {
8646    vasprintf(strp, fmt, args)
8647}
8648
8649// ============================================================
8650// scanf / vscanf / fscanf / vfscanf / sscanf / vsscanf
8651// ============================================================
8652
8653unsafe fn skip_ws(p: *const u8) -> *const u8 {
8654    let mut q = p;
8655    while *q == b' ' || *q == b'\t' || *q == b'\n' || *q == b'\r' { q = q.add(1); }
8656    q
8657}
8658
8659#[inline]
8660fn is_ws_byte(c: u8) -> bool {
8661    c == b' ' || c == b'\t' || c == b'\n' || c == b'\r' || c == 0x0b || c == 0x0c
8662}
8663
8664// Scan integer with position tracking. Returns (value, is_negative, success).
8665// base: 0=auto, 8=octal, 10=decimal, 16=hex
8666unsafe fn scan_int_val(
8667    buf: *const u8, pos: &mut usize, buf_len: usize, base: u32, width: usize,
8668) -> (u64, bool, bool) {
8669    let start = *pos;
8670    let max = if width > 0 { (start + width).min(buf_len) } else { buf_len };
8671    let neg = if *pos < max && *buf.add(*pos) == b'-' { *pos += 1; true }
8672    else if *pos < max && *buf.add(*pos) == b'+' { *pos += 1; false }
8673    else { false };
8674    if *pos >= max { *pos = start; return (0, false, false); }
8675    let mut base = base;
8676    let mut val: u64 = 0;
8677    // Auto-detect base / handle 0x prefix
8678    if (base == 0 || base == 16) && *pos < max && *buf.add(*pos) == b'0' {
8679        *pos += 1;
8680        if *pos < max && (*buf.add(*pos) == b'x' || *buf.add(*pos) == b'X') {
8681            *pos += 1;
8682            if *pos < max {
8683                if let Some(d) = hex_val(*buf.add(*pos)) {
8684                    val = d as u64; *pos += 1;
8685                    while *pos < max { if let Some(d) = hex_val(*buf.add(*pos)) { val = val.wrapping_mul(16).wrapping_add(d as u64); *pos += 1; } else { break; } }
8686                    return (val, neg, true);
8687                }
8688            }
8689            return (0, neg, false);
8690        } else if base == 0 {
8691            while *pos < max { let c = *buf.add(*pos); if c >= b'0' && c <= b'7' { val = val.wrapping_mul(8).wrapping_add((c - b'0') as u64); *pos += 1; } else { break; } }
8692            return (val, neg, true);
8693        } else {
8694            // base 16 with a leading zero ("00000000"): the zero is part of
8695            // the number, not a prefix that ends the conversion.
8696            while *pos < max {
8697                if let Some(d) = hex_val(*buf.add(*pos)) {
8698                    val = val.wrapping_mul(16).wrapping_add(d as u64);
8699                    *pos += 1;
8700                } else {
8701                    break;
8702                }
8703            }
8704            return (val, neg, true);
8705        }
8706    }
8707    if base == 0 { base = 10; }
8708    let mut found = false;
8709    while *pos < max {
8710        let c = *buf.add(*pos);
8711        let d: Option<u64> = match base {
8712            8 => if c >= b'0' && c <= b'7' { Some((c - b'0') as u64) } else { None },
8713            10 => if c >= b'0' && c <= b'9' { Some((c - b'0') as u64) } else { None },
8714            16 => hex_val(c).map(|v| v as u64),
8715            _ => None,
8716        };
8717        match d { Some(digit) => { val = val.wrapping_mul(base as u64).wrapping_add(digit); *pos += 1; found = true; } None => break }
8718    }
8719    if !found { *pos = start; return (0, false, false); }
8720    (val, neg, true)
8721}
8722
8723// Scan float with position tracking. Returns (value, success).
8724unsafe fn scan_float_val(
8725    buf: *const u8, pos: &mut usize, buf_len: usize, width: usize,
8726) -> (f64, bool) {
8727    let start = *pos;
8728    let max = if width > 0 { (start + width).min(buf_len) } else { buf_len };
8729    let neg = if *pos < max && *buf.add(*pos) == b'-' { *pos += 1; true }
8730    else if *pos < max && *buf.add(*pos) == b'+' { *pos += 1; false }
8731    else { false };
8732    // inf/nan
8733    if *pos + 2 < max {
8734        let (c0,c1,c2) = (*buf.add(*pos), *buf.add(*pos+1), *buf.add(*pos+2));
8735        if (c0==b'i'||c0==b'I')&&(c1==b'n'||c1==b'N')&&(c2==b'f'||c2==b'F') { *pos+=3; return (if neg{f64::NEG_INFINITY}else{f64::INFINITY}, true); }
8736        if (c0==b'n'||c0==b'N')&&(c1==b'a'||c1==b'A')&&(c2==b'n'||c2==b'N') { *pos+=3; return (if neg{-f64::NAN}else{f64::NAN}, true); }
8737    }
8738    // hex float
8739    if *pos+1 < max && *buf.add(*pos)==b'0' && (*buf.add(*pos+1)==b'x'||*buf.add(*pos+1)==b'X') {
8740        *pos += 2;
8741        let mut val: f64 = 0.0; let mut found = false; let mut frac_scale = 1.0f64; let mut in_frac = false;
8742        while *pos < max {
8743            if let Some(d) = hex_val(*buf.add(*pos)) {
8744                if in_frac { frac_scale /= 16.0; val += d as f64 * frac_scale; } else { val = val * 16.0 + d as f64; }
8745                *pos += 1; found = true;
8746            } else if *buf.add(*pos)==b'.' && !in_frac { in_frac = true; *pos += 1; } else { break; }
8747        }
8748        if !found { *pos = start; return (0.0, false); }
8749        // p exponent
8750        if *pos < max && (*buf.add(*pos)==b'p'||*buf.add(*pos)==b'P') {
8751            *pos += 1;
8752            let mut eneg = false;
8753            if *pos<max && *buf.add(*pos)==b'-' { eneg=true; *pos+=1; }
8754            else if *pos<max && *buf.add(*pos)==b'+' { *pos+=1; }
8755            let mut ev: i32 = 0; let mut ef = false;
8756            while *pos<max && *buf.add(*pos)>=b'0' && *buf.add(*pos)<=b'9' { ev = ev*10+(*buf.add(*pos)-b'0') as i32; *pos+=1; ef=true; }
8757            if !ef { return (0.0, false); }
8758            if eneg { ev = -ev; }
8759            val *= libm::pow(2.0, ev as f64);
8760        }
8761        return (if neg{-val}else{val}, true);
8762    }
8763    let token_start = start;
8764    let mut i = start;
8765    let mut saw_digit = false;
8766    let mut saw_dot = false;
8767    let mut saw_exp = false;
8768    let mut first = true;
8769    while i < max {
8770        let c = *buf.add(i);
8771        if (c == b'+' || c == b'-') && first {
8772            i += 1; first = false; continue;
8773        }
8774        if c >= b'0' && c <= b'9' {
8775            saw_digit = true; i += 1; first = false; continue;
8776        }
8777        if c == b'.' && !saw_dot && !saw_exp {
8778            saw_dot = true; i += 1; first = false; continue;
8779        }
8780        if (c == b'e' || c == b'E') && !saw_exp && saw_digit {
8781            saw_exp = true; i += 1; first = true; continue;
8782        }
8783        break;
8784    }
8785    if !saw_digit { return (0.0, false); }
8786    let s = core::str::from_utf8_unchecked(core::slice::from_raw_parts(buf.add(token_start), i - token_start));
8787    match <f64 as core::str::FromStr>::from_str(s) {
8788        Ok(v) => { *pos = i; return (v, true); }
8789        Err(_) => { return (0.0, false); }
8790    }
8791}
8792
8793// Comprehensive scanf parser with position tracking.
8794// consumed: if non-null, stores number of bytes consumed from buf.
8795/// Store a scanned integer with the width selected by the length modifier.
8796/// Without this, `%ld`/`%lu` silently truncate to 32 bits.
8797unsafe fn store_scan_int(dest: *mut c_void, len_mod: u8, val: u64, neg: bool) {
8798    let sv: i64 = if neg { (val as i64).wrapping_neg() } else { val as i64 };
8799    match len_mod {
8800        2 => *(dest as *mut i8) = sv as i8,
8801        1 => *(dest as *mut i16) = sv as i16,
8802        3 | 4 => *(dest as *mut i64) = sv,
8803        _ => *(dest as *mut i32) = sv as i32,
8804    }
8805}
8806
8807unsafe fn do_vsscanf(
8808    buf: *const u8, buf_len: usize, fmt: *const c_char, args: &mut VaList<'_>, consumed: *mut usize,
8809) -> c_int {
8810    let mut p = 0usize;
8811    let mut fi = 0usize;
8812    let mut assigned = 0i32;
8813    let mut input_eof = false;
8814    loop {
8815        let fc = *fmt.add(fi) as u8;
8816        if fc == 0 { break; }
8817        if is_ws_byte(fc) {
8818            while is_ws_byte(*fmt.add(fi) as u8) { fi += 1; }
8819            while p < buf_len && is_ws_byte(*buf.add(p)) { p += 1; }
8820            continue;
8821        }
8822        if fc != b'%' {
8823            if p >= buf_len { input_eof = true; break; }
8824            if *buf.add(p) != fc { break; }
8825            p += 1; fi += 1; continue;
8826        }
8827        fi += 1;
8828        // %%
8829        if *fmt.add(fi) as u8 == b'%' {
8830            if p >= buf_len { input_eof = true; break; }
8831            if *buf.add(p) != b'%' { break; }
8832            p += 1; fi += 1; continue;
8833        }
8834        let suppress = if *fmt.add(fi) as u8 == b'*' { fi += 1; true } else { false };
8835        let mut width: usize = 0;
8836        while (*fmt.add(fi) as u8) >= b'0' && (*fmt.add(fi) as u8) <= b'9' {
8837            width = width * 10 + ((*fmt.add(fi) as u8) - b'0') as usize; fi += 1;
8838        }
8839        if *fmt.add(fi) as u8 == b'm' { fi += 1; }
8840        let mut len_mod = 0u8;
8841        match *fmt.add(fi) as u8 {
8842            b'h' => { fi += 1; if *fmt.add(fi) as u8 == b'h' { fi += 1; len_mod = 2; } else { len_mod = 1; } }
8843            b'l' => { fi += 1; if *fmt.add(fi) as u8 == b'l' { fi += 1; len_mod = 4; } else { len_mod = 3; } }
8844            b'j' => { fi += 1; len_mod = 4; }
8845            b'z' | b't' => { fi += 1; len_mod = 3; }
8846            b'L' => { fi += 1; len_mod = 5; }
8847            _ => {}
8848        }
8849        let spec = *fmt.add(fi) as u8;
8850        let (real_spec, _real_len) = if spec==b'C' {(b'c',3u8)} else if spec==b'S' {(b's',3u8)} else {(spec,len_mod)};
8851        match real_spec {
8852            b'n' => {
8853                if !suppress {
8854                    let out = args.next_arg::<*mut c_void>();
8855                    if !out.is_null() {
8856                        match len_mod {
8857                            1 => *(out as *mut i16) = p as i16,
8858                            2 => *(out as *mut i8) = p as i8,
8859                            3 | 4 => *(out as *mut i64) = p as i64,
8860                            _ => *(out as *mut c_int) = p as c_int,
8861                        }
8862                    }
8863                }
8864            }
8865            b'd' | b'u' => {
8866                while p < buf_len && is_ws_byte(*buf.add(p)) { p += 1; }
8867                if p >= buf_len { input_eof = true; break; }
8868                let dest = if !suppress { Some(args.next_arg::<*mut c_void>()) } else { None };
8869                let (val, neg, ok) = scan_int_val(buf, &mut p, buf_len, 10, width);
8870                if !ok { break; }
8871                if let Some(out) = dest { if !out.is_null() {
8872                    store_scan_int(out, len_mod, val, neg);
8873                }}
8874                if !suppress { assigned += 1; }
8875            }
8876            b'i' => {
8877                while p < buf_len && is_ws_byte(*buf.add(p)) { p += 1; }
8878                if p >= buf_len { input_eof = true; break; }
8879                let dest = if !suppress { Some(args.next_arg::<*mut c_void>()) } else { None };
8880                let (val, neg, ok) = scan_int_val(buf, &mut p, buf_len, 0, width);
8881                if !ok { break; }
8882                if let Some(out) = dest { if !out.is_null() { store_scan_int(out, len_mod, val, neg); } }
8883                if !suppress { assigned += 1; }
8884            }
8885            b'o' => {
8886                while p < buf_len && is_ws_byte(*buf.add(p)) { p += 1; }
8887                if p >= buf_len { input_eof = true; break; }
8888                let dest = if !suppress { Some(args.next_arg::<*mut c_void>()) } else { None };
8889                let (val, neg, ok) = scan_int_val(buf, &mut p, buf_len, 8, width);
8890                if !ok { break; }
8891                if let Some(out) = dest { if !out.is_null() { store_scan_int(out, len_mod, val, neg); } }
8892                if !suppress { assigned += 1; }
8893            }
8894            b'x' | b'X' => {
8895                while p < buf_len && is_ws_byte(*buf.add(p)) { p += 1; }
8896                if p >= buf_len { input_eof = true; break; }
8897                let dest = if !suppress { Some(args.next_arg::<*mut c_void>()) } else { None };
8898                let (val, neg, ok) = scan_int_val(buf, &mut p, buf_len, 16, width);
8899                if !ok { break; }
8900                if let Some(out) = dest { if !out.is_null() { store_scan_int(out, len_mod, val, neg); } }
8901                if !suppress { assigned += 1; }
8902            }
8903            b'p' => {
8904                while p < buf_len && is_ws_byte(*buf.add(p)) { p += 1; }
8905                if p >= buf_len { input_eof = true; break; }
8906                let dest = if !suppress { Some(args.next_arg::<*mut *mut c_void>()) } else { None };
8907                let (val, _, ok) = scan_int_val(buf, &mut p, buf_len, 16, width);
8908                if !ok { break; }
8909                if let Some(out) = dest { if !out.is_null() { *out = val as usize as *mut c_void; } }
8910                if !suppress { assigned += 1; }
8911            }
8912            b'a' | b'e' | b'f' | b'g' | b'A' | b'E' | b'F' | b'G' => {
8913                while p < buf_len && is_ws_byte(*buf.add(p)) { p += 1; }
8914                if p >= buf_len { input_eof = true; break; }
8915                let (val, ok) = scan_float_val(buf, &mut p, buf_len, width);
8916                if !ok { break; }
8917                if !suppress {
8918                    if _real_len == 3 {
8919                        let out = args.next_arg::<*mut f64>(); if !out.is_null() { *out = val; }
8920                    } else if _real_len == 5 {
8921                        #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
8922                        { let out = args.next_arg::<*mut f128>(); if !out.is_null() { *out = val as f128; } }
8923                        #[cfg(target_arch = "x86_64")]
8924                        { let out = args.next_arg::<*mut f64>(); if !out.is_null() { *out = val; } }
8925                    } else {
8926                        let out = args.next_arg::<*mut f32>(); if !out.is_null() { *out = val as f32; }
8927                    }
8928                }
8929                if !suppress { assigned += 1; }
8930            }
8931            b'c' => {
8932                let w = if width > 0 { width } else { 1 };
8933                if p >= buf_len { input_eof = true; break; }
8934                let dest_ptr: *mut c_char = if !suppress { args.next_arg::<*mut c_char>() } else { core::ptr::null_mut() };
8935                let start_p = p;
8936                let mut j = 0usize;
8937                while j < w && p < buf_len {
8938                    if !dest_ptr.is_null() { *dest_ptr.add(j) = *buf.add(p) as c_char; }
8939                    p += 1; j += 1;
8940                }
8941                if j != w { p = start_p; input_eof = true; break; }
8942                if !suppress { assigned += 1; }
8943            }
8944            b's' => {
8945                while p < buf_len && is_ws_byte(*buf.add(p)) { p += 1; }
8946                if p >= buf_len { input_eof = true; break; }
8947                let dest_ptr: *mut c_char = if !suppress { args.next_arg::<*mut c_char>() } else { core::ptr::null_mut() };
8948                let w = if width > 0 { width } else { usize::MAX };
8949                let mut j = 0usize;
8950                while p < buf_len && *buf.add(p) != 0 && !is_ws_byte(*buf.add(p)) && j < w {
8951                    if !dest_ptr.is_null() { *dest_ptr.add(j) = *buf.add(p) as c_char; }
8952                    p += 1; j += 1;
8953                }
8954                if j == 0 { break; }
8955                if !dest_ptr.is_null() { *dest_ptr.add(j) = 0; }
8956                if !suppress { assigned += 1; }
8957            }
8958            b'[' => {
8959                fi += 1;
8960                let negate = if *fmt.add(fi) as u8 == b'^' { fi += 1; true } else { false };
8961                let mut charset = [0u8; 256];
8962                let mut prev: Option<u8> = None;
8963                if *fmt.add(fi) as u8 == b']' {
8964                    charset[b']' as usize] = 1;
8965                    prev = Some(b']');
8966                    fi += 1;
8967                }
8968                loop {
8969                    let c = *fmt.add(fi) as u8;
8970                    if c == b']' || c == 0 { break; }
8971                    // "a-z" is a range; a '-' next to ']' or at the start/end
8972                    // of the set is a literal.
8973                    if c == b'-' && prev.is_some() {
8974                        let hi = *fmt.add(fi + 1) as u8;
8975                        if hi != b']' && hi != 0 {
8976                            let lo = prev.unwrap();
8977                            let mut k = lo;
8978                            while k <= hi {
8979                                charset[k as usize] = 1;
8980                                if k == 255 { break; }
8981                                k += 1;
8982                            }
8983                            prev = None;
8984                            fi += 2;
8985                            continue;
8986                        }
8987                    }
8988                    charset[c as usize] = 1;
8989                    prev = Some(c);
8990                    fi += 1;
8991                }
8992                if *fmt.add(fi) as u8 == b']' { fi += 1; }
8993                fi -= 1; // common fi += 1 will advance past the closing ]
8994                if p >= buf_len { input_eof = true; break; }
8995                let dest_ptr: *mut c_char = if !suppress { args.next_arg::<*mut c_char>() } else { core::ptr::null_mut() };
8996                let w = if width > 0 { width } else { usize::MAX };
8997                let mut j = 0usize;
8998                while p < buf_len && *buf.add(p) != 0 && j < w {
8999                    let c = *buf.add(p);
9000                    let in_set = charset[c as usize] != 0;
9001                    if negate { if in_set { break; } } else { if !in_set { break; } }
9002                    if !dest_ptr.is_null() { *dest_ptr.add(j) = c as c_char; }
9003                    p += 1; j += 1;
9004                }
9005                if j == 0 { break; }
9006                if !dest_ptr.is_null() { *dest_ptr.add(j) = 0; }
9007                if !suppress { assigned += 1; }
9008            }
9009            _ => break,
9010        }
9011        fi += 1;
9012    }
9013    if !consumed.is_null() { *consumed = p; }
9014    if assigned == 0 && input_eof { -1 } else { assigned }
9015}
9016
9017unsafe fn vsscanf_inner(buf: *const u8, fmt: *const c_char, args: &mut VaList<'_>) -> c_int {
9018    let len = strlen(buf as *const c_char);
9019    do_vsscanf(buf, len, fmt, args, core::ptr::null_mut())
9020}
9021
9022#[no_mangle]
9023pub unsafe extern "C" fn vsscanf(buf: *const c_char, fmt: *const c_char, mut args: VaList) -> c_int {
9024    vsscanf_inner(buf as *const u8, fmt, &mut args)
9025}
9026
9027#[no_mangle]
9028pub unsafe extern "C" fn vfscanf(stream: *mut FILE, fmt: *const c_char, mut args: VaList) -> c_int {
9029    let f = &mut *stream;
9030    let start_ungotten_count = f.ungotten_count as usize;
9031    let ungotten_saved = f.ungotten;
9032    let sfunc = f.seek_fn;
9033    // Logical position, not the raw fd offset: fgetc may have read ahead.
9034    let start_pos = if sfunc.is_some() { ftello(stream) } else { -1 };
9035
9036    let mut line = [0u8; 4096];
9037    let mut pos = 0usize;
9038
9039    if start_pos >= 0 {
9040        // Seekable: read via fgetc until \n or EOF (existing behavior)
9041        while pos < line.len() - 1 {
9042            let c = fgetc(stream);
9043            if c == -1 { break; }
9044            line[pos] = c as u8;
9045            pos += 1;
9046            if c == b'\n' as c_int { break; }
9047        }
9048    } else {
9049        // Non-seekable (pipe/socket): drain pushback, then read all available
9050        // data non-blocking so we don't stop at \n boundaries.
9051
9052        // 1. Drain ungotten bytes (LIFO order)
9053        while f.ungotten_count > 0 && pos < line.len() - 1 {
9054            f.ungotten_count -= 1;
9055            line[pos] = f.ungotten[f.ungotten_count as usize] as u8;
9056            pos += 1;
9057        }
9058        // 2. Drain rpos/rend pushback buffer
9059        while !f.rpos.is_null() && f.rpos < f.rend && pos < line.len() - 1 {
9060            line[pos] = *f.rpos;
9061            f.rpos = f.rpos.add(1);
9062            pos += 1;
9063        }
9064        // 3. Wait for the first byte (blocking), then drain whatever else is
9065        // available without waiting. A pure non-blocking read would race with
9066        // a producer such as popen() that has not written yet.
9067        let fd = f.fd;
9068        if pos == 0 {
9069            let n = sys_read(fd as i64, line.as_mut_ptr(), 1);
9070            if n == 1 { pos = 1; }
9071        }
9072        let old_flags = sys_fcntl(fd, F_GETFL, 0);
9073        if old_flags >= 0 {
9074            let _ = sys_fcntl(fd, F_SETFL, old_flags | O_NONBLOCK as i64);
9075        }
9076        while pos < line.len() - 1 {
9077            let n = sys_read(fd as i64, line.as_mut_ptr().add(pos), line.len() - 1 - pos);
9078            if n <= 0 { break; }
9079            pos += n as usize;
9080        }
9081        if old_flags >= 0 {
9082            let _ = sys_fcntl(fd, F_SETFL, old_flags);
9083        }
9084        // Clear flags that the failed non-blocking read may have set
9085        f._eof = 0;
9086        f._err = 0;
9087        f.flags &= !(F_EOF | F_ERR);
9088    }
9089
9090    line[pos] = 0;
9091    if pos == 0 { return 0; }
9092
9093    let mut consumed = 0usize;
9094    let assigned = do_vsscanf(line.as_ptr(), pos, fmt, &mut args, &mut consumed);
9095
9096    if start_pos >= 0 {
9097        // Seek back to the logical position just past the consumed bytes and
9098        // restore any ungetc pushback the scan did not consume.
9099        let remaining = start_ungotten_count.saturating_sub(consumed);
9100        // The restored ungetc bytes will be subtracted by ftello(), so the
9101        // raw fd offset must include them.
9102        let target = start_pos + consumed as i64 + remaining as i64;
9103        if let Some(sfunc) = sfunc {
9104            sfunc(stream, target, SEEK_SET);
9105        }
9106        f.rpos = core::ptr::null_mut();
9107        f.rend = core::ptr::null_mut();
9108        if consumed < pos { f._eof = 0; }
9109        f.ungotten_count = 0;
9110        for i in 0..remaining {
9111            f.ungotten[f.ungotten_count as usize] = ungotten_saved[i];
9112            f.ungotten_count += 1;
9113        }
9114    } else {
9115        // Non-seekable: push back unconsumed bytes into FILE read buffer
9116        let unconsumed = pos.saturating_sub(consumed);
9117        if unconsumed > 0 && !f.buf.is_null() {
9118            let copy_count = core::cmp::min(unconsumed, f.buf_size);
9119            core::ptr::copy(
9120                line.as_ptr().add(consumed),
9121                f.buf,
9122                copy_count,
9123            );
9124            f.rpos = f.buf;
9125            f.rend = f.buf.add(copy_count);
9126        }
9127        f.ungotten_count = 0;
9128        f._eof = 0;
9129        f.flags &= !F_EOF;
9130    }
9131    assigned
9132}
9133
9134#[no_mangle]
9135pub unsafe extern "C" fn vscanf(fmt: *const c_char, args: VaList) -> c_int {
9136    vfscanf(stdin, fmt, args)
9137}
9138
9139#[no_mangle]
9140pub unsafe extern "C" fn sscanf(buf: *const c_char, fmt: *const c_char, mut args: ...) -> c_int {
9141    vsscanf_inner(buf as *const u8, fmt, &mut args)
9142}
9143
9144#[no_mangle]
9145pub unsafe extern "C" fn fscanf(stream: *mut FILE, fmt: *const c_char, args: ...) -> c_int {
9146    vfscanf(stream, fmt, args)
9147}
9148
9149#[no_mangle]
9150pub unsafe extern "C" fn scanf(fmt: *const c_char, args: ...) -> c_int {
9151    vfscanf(stdin, fmt, args)
9152}
9153
9154// ============================================================
9155// getdelim / getline
9156// ============================================================
9157
9158#[no_mangle]
9159pub unsafe extern "C" fn getdelim(
9160    lineptr: *mut *mut c_char, n: *mut usize, delim: c_int, stream: *mut FILE,
9161) -> isize {
9162    if lineptr.is_null() || n.is_null() { return -1; }
9163    let mut cap = *n;
9164    let mut buf = *lineptr;
9165    if buf.is_null() || cap == 0 {
9166        cap = 128;
9167        buf = realloc(buf as *mut c_void, cap) as *mut c_char;
9168        if buf.is_null() { return -1; }
9169        *lineptr = buf;
9170        *n = cap;
9171    }
9172    let mut len = 0usize;
9173    loop {
9174        let c = fgetc(stream);
9175        if c == -1 { if len == 0 { return -1; } break; }
9176        if len + 1 >= cap {
9177            let new_cap = cap.wrapping_mul(2);
9178            let new_buf = realloc(buf as *mut c_void, new_cap) as *mut c_char;
9179            if new_buf.is_null() { return -1; }
9180            buf = new_buf; cap = new_cap;
9181            *lineptr = buf; *n = cap;
9182        }
9183        *buf.add(len) = c as c_char;
9184        len += 1;
9185        if c == delim { break; }
9186    }
9187    *buf.add(len) = 0;
9188    len as isize
9189}
9190
9191#[no_mangle]
9192pub unsafe extern "C" fn getline(lineptr: *mut *mut c_char, n: *mut usize, stream: *mut FILE) -> isize {
9193    getdelim(lineptr, n, b'\n' as c_int, stream)
9194}
9195
9196// ============================================================
9197// popen / pclose
9198// ============================================================
9199
9200#[no_mangle]
9201pub unsafe extern "C" fn popen(cmd: *const c_char, mode: *const c_char) -> *mut FILE {
9202    if cmd.is_null() || mode.is_null() { ERRNO!() = EINVAL; return core::ptr::null_mut(); }
9203    let m = *mode;
9204    if m != b'r' as c_char && m != b'w' as c_char { ERRNO!() = EINVAL; return core::ptr::null_mut(); }
9205    let mut p: [c_int; 2] = [0; 2];
9206    if sys_pipe2(p.as_mut_ptr(), O_CLOEXEC) < 0 { return core::ptr::null_mut(); }
9207    let op = if m == b'r' as c_char { 0 } else { 1 };
9208    let child_fd = p[1 - op];
9209    let f = fdopen(p[op], mode);
9210    if f.is_null() { sys_close(p[0] as i64); sys_close(p[1] as i64); return core::ptr::null_mut(); }
9211    let pid = sys_fork();
9212    if pid < 0 { fclose(f); sys_close(child_fd as i64); return core::ptr::null_mut(); }
9213    if pid == 0 {
9214        sys_close(p[op] as i64);
9215        if child_fd != (1 - op) as c_int { sys_dup3(child_fd, (1 - op) as c_int, 0); sys_close(child_fd as i64); }
9216        let sh = b"/bin/sh\0".as_ptr() as *const c_char;
9217        let argv = [b"sh\0".as_ptr() as *const c_char, b"-c\0".as_ptr() as *const c_char, cmd, core::ptr::null()];
9218        sys_execve(sh, argv.as_ptr(), __environ as *const *const c_char);
9219        _exit(127);
9220    }
9221    sys_close(child_fd as i64);
9222    (*f).pipe_pid = pid as c_int;
9223    f
9224}
9225
9226#[no_mangle]
9227pub unsafe extern "C" fn pclose(f: *mut FILE) -> c_int {
9228    let pid = (*f).pipe_pid;
9229    let r = fclose(f);
9230    if pid <= 0 { return -1; }
9231    let mut status: c_int = 0;
9232    let mut w: c_int;
9233    loop {
9234        w = waitpid(pid, &mut status, 0);
9235        if w >= 0 || ERRNO!() != EINTR_VAL { break; }
9236    }
9237    if w < 0 || r != 0 { return -1; }
9238    status
9239}
9240
9241// ============================================================
9242// tmpfile / tmpnam / remove / rename
9243// ============================================================
9244
9245const L_TMPNAM: usize = 20;
9246static mut TMPNAM_COUNTER: c_uint = 0;
9247
9248unsafe fn fill_tmpname(out: *mut c_char, seed: c_uint) {
9249    let prefix = b"/tmp/tmp\0";
9250    let mut k = 0;
9251    while k < prefix.len() - 1 { *out.add(k) = prefix[k] as c_char; k += 1; }
9252    let (buf, len) = format_hex(seed as u64, false);
9253    for j in 0..len.min(L_TMPNAM - k - 1) { *out.add(k + j) = buf[j] as c_char; k += 1; }
9254    *out.add(k) = 0;
9255}
9256
9257#[no_mangle]
9258pub unsafe extern "C" fn tmpfile() -> *mut FILE {
9259    let mut name = [0u8; L_TMPNAM];
9260    let mut ctr: c_uint = 0;
9261    loop {
9262        ctr = ctr.wrapping_add(1);
9263        let mut ts: timespec = core::mem::zeroed();
9264        let _ = sys_clock_gettime(CLOCK_REALTIME, &mut ts);
9265        let seed = (ts.tv_nsec as c_uint).wrapping_add(ctr);
9266        fill_tmpname(name.as_mut_ptr() as *mut c_char, seed);
9267        let fd = sys_open(name.as_ptr(), (O_RDWR | O_CREAT | O_EXCL) as i64, 0o600);
9268        if fd >= 0 {
9269            sys_unlinkat(AT_FDCWD, name.as_ptr(), 0);
9270            let f = fdopen(fd as c_int, b"w+\0".as_ptr() as *const c_char);
9271            if f.is_null() { sys_close(fd); }
9272            return f;
9273        }
9274        if ctr > 100 { return core::ptr::null_mut(); }
9275    }
9276}
9277
9278#[no_mangle]
9279pub unsafe extern "C" fn tmpnam(s: *mut c_char) -> *mut c_char {
9280    static mut BUF: [c_char; L_TMPNAM] = [0; L_TMPNAM];
9281    let out = if s.is_null() { core::ptr::addr_of_mut!(BUF) as *mut c_char } else { s };
9282    TMPNAM_COUNTER = TMPNAM_COUNTER.wrapping_add(1);
9283    let mut ts: timespec = core::mem::zeroed();
9284    let _ = sys_clock_gettime(CLOCK_REALTIME, &mut ts);
9285    let seed = (ts.tv_nsec as c_uint).wrapping_add(TMPNAM_COUNTER);
9286    fill_tmpname(out, seed);
9287    out
9288}
9289
9290#[no_mangle]
9291pub unsafe extern "C" fn remove(path: *const c_char) -> c_int {
9292    if path.is_null() { ERRNO!() = EINVAL; return -1; }
9293    let r = sys_unlinkat(AT_FDCWD, path as *const u8, 0);
9294    if r == 0 {
9295        return 0;
9296    }
9297    // POSIX remove() also removes directories; musl falls back to rmdir only
9298    // when unlink reports EISDIR.
9299    if (-r) as c_int == 21 {
9300        return rmdir(path);
9301    }
9302    ERRNO!() = (-r) as c_int;
9303    -1
9304}
9305
9306#[no_mangle]
9307pub unsafe extern "C" fn rename(old: *const c_char, new_: *const c_char) -> c_int {
9308    if old.is_null() || new_.is_null() { ERRNO!() = EINVAL; return -1; }
9309    let r = sys_renameat2(AT_FDCWD, old as *const u8, AT_FDCWD, new_ as *const u8, 0);
9310    if r == 0 { 0 } else { ERRNO!() = (-r) as c_int; -1 }
9311}
9312
9313// ============================================================
9314// Allocator basics
9315// ============================================================
9316
9317const MMAP_FAILED: *mut u8 = !0usize as *mut u8;
9318const PAGE: usize = 4096;
9319
9320// ============================================================
9321// Process: exit / _exit / _Exit
9322// ============================================================
9323
9324#[no_mangle]
9325pub unsafe extern "C" fn _exit(code: c_int) -> ! {
9326    sys_exit_group(code)
9327}
9328
9329#[inline]
9330unsafe fn sys_exit_group(code: c_int) -> ! {
9331    <Arch as Syscalls>::syscall_noreturn1(SYS_EXIT_GROUP, code as i64)
9332}
9333
9334#[inline]
9335unsafe fn sys_exit_thread(code: c_int) -> ! {
9336    <Arch as Syscalls>::syscall_noreturn1(SYS_EXIT, code as i64)
9337}
9338
9339#[no_mangle]
9340pub unsafe extern "C" fn _Exit(code: c_int) -> ! {
9341    _exit(code);
9342}
9343
9344#[no_mangle]
9345pub unsafe extern "C" fn exit(code: c_int) -> ! {
9346    fflush(core::ptr::null_mut());
9347    __funcs_on_exit();
9348    _exit(code);
9349}
9350
9351#[no_mangle]
9352pub unsafe extern "C" fn abort() -> ! {
9353    raise(6); // SIGABRT
9354    _exit(128 + 6);
9355}
9356
9357// aarch64 GCC accesses the stack canary via the global __stack_chk_guard
9358// symbol (GOT-based for PIE). Export it so the dynamic linker can resolve it.
9359#[no_mangle]
9360pub static mut __stack_chk_guard: usize = 0;
9361
9362#[no_mangle]
9363pub unsafe extern "C" fn __stack_chk_fail() -> ! {
9364    const MSG: &[u8] = b"*** stack smashing detected ***: terminated\n";
9365    let _ = sys_write(2, MSG.as_ptr(), MSG.len());
9366    abort();
9367}
9368
9369#[no_mangle]
9370pub unsafe extern "C" fn __stack_chk_fail_local() -> ! {
9371    __stack_chk_fail()
9372}
9373
9374// ============================================================
9375// qsort / qsort_r (smoothsort, O(1) aux, adaptive)
9376// ============================================================
9377
9378type CmpRfn = unsafe extern "C" fn(*const c_void, *const c_void, *mut c_void) -> c_int;
9379
9380const AR_LEN: usize = 16 * core::mem::size_of::<usize>();
9381const AR_MASK: usize = AR_LEN - 1;
9382
9383unsafe fn qsort_pntz(p: &[usize; 2]) -> i32 {
9384    if p[0] != 1 {
9385        p[0].wrapping_sub(1).trailing_zeros() as i32
9386    } else if p[1] != 0 {
9387        (8 * core::mem::size_of::<usize>()) as i32 + p[1].trailing_zeros() as i32
9388    } else {
9389        0
9390    }
9391}
9392
9393unsafe fn qsort_cycle(width: usize, ar: &mut [*mut u8; AR_LEN], n: usize) {
9394    if n < 2 { return; }
9395    let mut tmp = [0u8; 256];
9396    ar[n] = tmp.as_mut_ptr();
9397    let mut w = width;
9398    while w > 0 {
9399        let l = if 256 < w { 256 } else { w };
9400        core::ptr::copy_nonoverlapping(ar[0], ar[n], l);
9401        for i in 0..n {
9402            core::ptr::copy_nonoverlapping(ar[i + 1], ar[i], l);
9403            ar[i] = ar[i].add(l);
9404        }
9405        w -= l;
9406    }
9407}
9408
9409unsafe fn qsort_shl(p: &mut [usize; 2], n: i32) {
9410    let bits = (8 * core::mem::size_of::<usize>()) as i32;
9411    if n >= bits {
9412        p[1] = p[0];
9413        p[0] = 0;
9414        let n = n - bits;
9415        if n == 0 { return; }
9416    }
9417    p[1] = (p[1] << n) | (p[0] >> (bits - n));
9418    p[0] <<= n;
9419}
9420
9421unsafe fn qsort_shr(p: &mut [usize; 2], n: i32) {
9422    let bits = (8 * core::mem::size_of::<usize>()) as i32;
9423    if n >= bits {
9424        p[0] = p[1];
9425        p[1] = 0;
9426        let n = n - bits;
9427        if n == 0 { return; }
9428    }
9429    p[0] = (p[0] >> n) | (p[1] << (bits - n));
9430    p[1] >>= n;
9431}
9432
9433unsafe fn qsort_sift(
9434    head: *mut u8, width: usize,
9435    cmp: CmpRfn, arg: *mut c_void,
9436    pshift: i32, lp: &[usize],
9437) {
9438    let mut ar: [*mut u8; AR_LEN] = [core::ptr::null_mut(); AR_LEN];
9439    ar[0] = head;
9440    let mut i = 1usize;
9441    let mut h = head;
9442    let mut ps = pshift;
9443
9444    while ps > 1 {
9445        let rt = h.sub(width);
9446        let lf = h.sub(width + lp[(ps - 2) as usize]);
9447
9448        if cmp(ar[0] as *const c_void, lf as *const c_void, arg) >= 0
9449            && cmp(ar[0] as *const c_void, rt as *const c_void, arg) >= 0
9450        {
9451            break;
9452        }
9453        if cmp(lf as *const c_void, rt as *const c_void, arg) >= 0 {
9454            ar[i & AR_MASK] = lf;
9455            i += 1;
9456            h = lf;
9457            ps -= 1;
9458        } else {
9459            ar[i & AR_MASK] = rt;
9460            i += 1;
9461            h = rt;
9462            ps -= 2;
9463        }
9464    }
9465    qsort_cycle(width, &mut ar, i & AR_MASK);
9466}
9467
9468unsafe fn qsort_trinkle(
9469    head: *mut u8, width: usize,
9470    cmp: CmpRfn, arg: *mut c_void,
9471    pp: &[usize; 2], pshift: i32, trusty: i32,
9472    lp: &[usize],
9473) {
9474    let mut ar: [*mut u8; AR_LEN] = [core::ptr::null_mut(); AR_LEN];
9475    let mut p = *pp;
9476    ar[0] = head;
9477    let mut i = 1usize;
9478    let mut h = head;
9479    let mut ps = pshift;
9480    let mut t = trusty;
9481
9482    while p[0] != 1 || p[1] != 0 {
9483        let stepson = h.sub(lp[ps as usize]);
9484        if cmp(stepson as *const c_void, ar[0] as *const c_void, arg) <= 0 {
9485            break;
9486        }
9487        if t == 0 && ps > 1 {
9488            let rt = h.sub(width);
9489            let lf = h.sub(width + lp[(ps - 2) as usize]);
9490            if cmp(rt as *const c_void, stepson as *const c_void, arg) >= 0
9491                || cmp(lf as *const c_void, stepson as *const c_void, arg) >= 0
9492            {
9493                break;
9494            }
9495        }
9496
9497        ar[i & AR_MASK] = stepson;
9498        i += 1;
9499        h = stepson;
9500        let trail = qsort_pntz(&p);
9501        qsort_shr(&mut p, trail);
9502        ps += trail;
9503        t = 0;
9504    }
9505    if t == 0 {
9506        qsort_cycle(width, &mut ar, i & AR_MASK);
9507        qsort_sift(h, width, cmp, arg, ps, lp);
9508    }
9509}
9510
9511#[no_mangle]
9512pub unsafe extern "C" fn __qsort_r(
9513    base: *mut c_void,
9514    nel: usize,
9515    width: usize,
9516    cmp: CmpRfn,
9517    arg: *mut c_void,
9518) {
9519    let size = width * nel;
9520    if size == 0 { return; }
9521
9522    let mut lp = [0usize; 96];
9523    lp[0] = width;
9524    lp[1] = width;
9525    let mut li = 2;
9526    while {
9527        lp[li] = lp[li - 2] + lp[li - 1] + width;
9528        lp[li] < size
9529    } {
9530        li += 1;
9531    }
9532
9533    let mut head = base as *mut u8;
9534    let high = head.add(size - width);
9535    let mut p: [usize; 2] = [1, 0];
9536    let mut pshift: i32 = 1;
9537
9538    while head < high {
9539        if (p[0] & 3) == 3 {
9540            qsort_sift(head, width, cmp, arg, pshift, &lp);
9541            qsort_shr(&mut p, 2);
9542            pshift += 2;
9543        } else {
9544            if lp[(pshift - 1) as usize] >= (high as usize) - (head as usize) {
9545                qsort_trinkle(head, width, cmp, arg, &p, pshift, 0, &lp);
9546            } else {
9547                qsort_sift(head, width, cmp, arg, pshift, &lp);
9548            }
9549
9550            if pshift == 1 {
9551                qsort_shl(&mut p, 1);
9552                pshift = 0;
9553            } else {
9554                qsort_shl(&mut p, pshift - 1);
9555                pshift = 1;
9556            }
9557        }
9558
9559        p[0] |= 1;
9560        head = head.add(width);
9561    }
9562
9563    qsort_trinkle(head, width, cmp, arg, &p, pshift, 0, &lp);
9564
9565    while pshift != 1 || p[0] != 1 || p[1] != 0 {
9566        if pshift <= 1 {
9567            let trail = qsort_pntz(&p);
9568            qsort_shr(&mut p, trail);
9569            pshift += trail;
9570        } else {
9571            qsort_shl(&mut p, 2);
9572            pshift -= 2;
9573            p[0] ^= 7;
9574            qsort_shr(&mut p, 1);
9575            qsort_trinkle(
9576                head.sub(lp[pshift as usize] + width),
9577                width, cmp, arg, &p, pshift + 1, 1, &lp,
9578            );
9579            qsort_shl(&mut p, 1);
9580            p[0] |= 1;
9581            qsort_trinkle(head.sub(width), width, cmp, arg, &p, pshift, 1, &lp);
9582        }
9583        head = head.sub(width);
9584    }
9585}
9586
9587#[no_mangle]
9588pub unsafe extern "C" fn qsort_r(
9589    base: *mut c_void,
9590    nel: usize,
9591    width: usize,
9592    cmp: CmpRfn,
9593    arg: *mut c_void,
9594) {
9595    __qsort_r(base, nel, width, cmp, arg);
9596}
9597
9598unsafe extern "C" fn qsort_wrap_cmp(
9599    a: *const c_void,
9600    b: *const c_void,
9601    cmp_ctx: *mut c_void,
9602) -> c_int {
9603    let cmp: CmpFn = core::mem::transmute(cmp_ctx);
9604    cmp(a, b)
9605}
9606
9607#[no_mangle]
9608pub unsafe extern "C" fn qsort(
9609    base: *mut c_void,
9610    nel: usize,
9611    width: usize,
9612    cmp: CmpFn,
9613) {
9614    __qsort_r(base, nel, width, qsort_wrap_cmp, cmp as *mut c_void);
9615}
9616
9617// ============================================================
9618// atexit / __cxa_atexit / __funcs_on_exit
9619// ============================================================
9620
9621const ATEXIT_COUNT: usize = 32;
9622
9623#[repr(C)]
9624struct AtExitBlock {
9625    next: *mut AtExitBlock,
9626    f: [Option<unsafe extern "C" fn(*mut c_void)>; ATEXIT_COUNT],
9627    a: [*mut c_void; ATEXIT_COUNT],
9628}
9629
9630static mut ATEXIT_BUILTIN: AtExitBlock = AtExitBlock {
9631    next: core::ptr::null_mut(),
9632    f: [None; ATEXIT_COUNT],
9633    a: [core::ptr::null_mut(); ATEXIT_COUNT],
9634};
9635static mut ATEXIT_HEAD: *mut AtExitBlock = core::ptr::null_mut();
9636static mut ATEXIT_SLOT: usize = 0;
9637static mut ATEXIT_FINISHED: bool = false;
9638
9639#[no_mangle]
9640pub unsafe extern "C" fn __funcs_on_exit() {
9641    while !ATEXIT_HEAD.is_null() {
9642        let head = ATEXIT_HEAD;
9643        while ATEXIT_SLOT > 0 {
9644            ATEXIT_SLOT -= 1;
9645            if let Some(f) = (*head).f[ATEXIT_SLOT] {
9646                let a = (*head).a[ATEXIT_SLOT];
9647                f(a);
9648            }
9649        }
9650        ATEXIT_HEAD = (*head).next;
9651        ATEXIT_SLOT = ATEXIT_COUNT;
9652    }
9653    ATEXIT_FINISHED = true;
9654}
9655
9656#[no_mangle]
9657pub unsafe extern "C" fn __cxa_atexit(
9658    func: unsafe extern "C" fn(*mut c_void),
9659    arg: *mut c_void,
9660    _dso: *mut c_void,
9661) -> c_int {
9662    if ATEXIT_FINISHED {
9663        return -1;
9664    }
9665    if ATEXIT_HEAD.is_null() {
9666        ATEXIT_HEAD = core::ptr::addr_of_mut!(ATEXIT_BUILTIN);
9667    }
9668    if ATEXIT_SLOT == ATEXIT_COUNT {
9669        let new = calloc(1, core::mem::size_of::<AtExitBlock>()) as *mut AtExitBlock;
9670        if new.is_null() {
9671            return -1;
9672        }
9673        (*new).next = ATEXIT_HEAD;
9674        ATEXIT_HEAD = new;
9675        ATEXIT_SLOT = 0;
9676    }
9677    (*ATEXIT_HEAD).f[ATEXIT_SLOT] = Some(func);
9678    (*ATEXIT_HEAD).a[ATEXIT_SLOT] = arg;
9679    ATEXIT_SLOT += 1;
9680    0
9681}
9682
9683unsafe extern "C" fn atexit_caller(arg: *mut c_void) {
9684    let func: unsafe extern "C" fn() = core::mem::transmute(arg);
9685    func();
9686}
9687
9688#[no_mangle]
9689pub unsafe extern "C" fn atexit(func: unsafe extern "C" fn()) -> c_int {
9690    __cxa_atexit(atexit_caller, func as *mut c_void, core::ptr::null_mut())
9691}
9692
9693#[no_mangle]
9694pub unsafe extern "C" fn __cxa_finalize(_dso: *mut c_void) {}
9695
9696// ============================================================
9697// getenv / setenv / putenv / unsetenv / clearenv
9698// ============================================================
9699
9700#[no_mangle]
9701pub static mut __environ: *mut *mut c_char = core::ptr::null_mut();
9702
9703// ponytail: simple env tracking for allocated entries; O(n) scan
9704const ENV_MAX_ALLOCED: usize = 256;
9705static mut ENV_ALLOCED: [*mut c_char; ENV_MAX_ALLOCED] = [core::ptr::null_mut(); ENV_MAX_ALLOCED];
9706static mut ENV_ALLOCED_N: usize = 0;
9707
9708unsafe fn env_rm_add(old: *mut c_char, mut new: *mut c_char) {
9709    for i in 0..ENV_ALLOCED_N {
9710        if ENV_ALLOCED[i] == old {
9711            ENV_ALLOCED[i] = new;
9712            if !old.is_null() {
9713                free(old as *mut c_void);
9714            }
9715            return;
9716        } else if ENV_ALLOCED[i].is_null() && !new.is_null() {
9717            ENV_ALLOCED[i] = new;
9718            new = core::ptr::null_mut();
9719        }
9720    }
9721    if !new.is_null() && ENV_ALLOCED_N < ENV_MAX_ALLOCED {
9722        ENV_ALLOCED[ENV_ALLOCED_N] = new;
9723        ENV_ALLOCED_N += 1;
9724    }
9725}
9726
9727unsafe fn strchrnul_u8(s: *const u8, c: u8) -> *const u8 {
9728    let mut p = s;
9729    while *p != 0 && *p != c {
9730        p = p.add(1);
9731    }
9732    p
9733}
9734
9735#[no_mangle]
9736pub unsafe extern "C" fn getenv(name: *const c_char) -> *mut c_char {
9737    if __environ.is_null() || name.is_null() {
9738        return core::ptr::null_mut();
9739    }
9740    let name = name as *const u8;
9741    let l = strchrnul_u8(name, b'=') as usize - name as usize;
9742    if l == 0 || *name.add(l) != 0 {
9743        return core::ptr::null_mut();
9744    }
9745    let mut e = __environ;
9746    while !(*e).is_null() {
9747        let entry = *e as *const u8;
9748        if strncmp(entry, name, l) == 0 && *entry.add(l) == b'=' {
9749            return entry.add(l + 1) as *mut c_char;
9750        }
9751        e = e.add(1);
9752    }
9753    core::ptr::null_mut()
9754}
9755
9756#[no_mangle]
9757pub unsafe extern "C" fn setenv(
9758    var: *const c_char,
9759    value: *const c_char,
9760    overwrite: c_int,
9761) -> c_int {
9762    if var.is_null() || value.is_null() {
9763        ERRNO!() = EINVAL;
9764        return -1;
9765    }
9766    let var = var as *const u8;
9767    let l1 = strchrnul_u8(var, b'=') as usize - var as usize;
9768    if l1 == 0 || *var.add(l1) != 0 {
9769        ERRNO!() = EINVAL;
9770        return -1;
9771    }
9772    if overwrite == 0 && !getenv(var as *const c_char).is_null() {
9773        return 0;
9774    }
9775    let l2 = strlen(value as *const c_char);
9776    let s = malloc(l1 + l2 + 2) as *mut u8;
9777    if s.is_null() {
9778        return -1;
9779    }
9780    core::ptr::copy_nonoverlapping(var, s, l1);
9781    *s.add(l1) = b'=';
9782    core::ptr::copy_nonoverlapping(value as *const u8, s.add(l1 + 1), l2 + 1);
9783    putenv_internal(s as *mut c_char, l1, s as *mut c_char)
9784}
9785
9786unsafe fn putenv_internal(s: *mut c_char, l: usize, r: *mut c_char) -> c_int {
9787    let mut i = 0;
9788    if !__environ.is_null() {
9789        let mut e = __environ;
9790        while !(*e).is_null() {
9791            if strncmp(s as *const u8, *e as *const u8, l + 1) == 0 {
9792                let tmp = *e;
9793                *e = s;
9794                env_rm_add(tmp, r);
9795                return 0;
9796            }
9797            e = e.add(1);
9798            i += 1;
9799        }
9800    }
9801    let newenv = malloc(core::mem::size_of::<*mut c_char>() * (i + 2)) as *mut *mut c_char;
9802    if newenv.is_null() {
9803        if !r.is_null() { free(r as *mut c_void); }
9804        return -1;
9805    }
9806    if i > 0 && !__environ.is_null() {
9807        core::ptr::copy_nonoverlapping(__environ, newenv, i);
9808    }
9809    *newenv.add(i) = s;
9810    *newenv.add(i + 1) = core::ptr::null_mut();
9811    // ponytail: don't free old __environ (may not be from our malloc)
9812    __environ = newenv;
9813    environ = __environ;
9814    if !r.is_null() {
9815        env_rm_add(core::ptr::null_mut(), r);
9816    }
9817    0
9818}
9819
9820#[no_mangle]
9821pub unsafe extern "C" fn putenv(s: *mut c_char) -> c_int {
9822    if s.is_null() { return -1; }
9823    let l = strchrnul_u8(s as *const u8, b'=') as usize - s as *const u8 as usize;
9824    if l == 0 || *s.add(l) as u8 == 0 {
9825        return unsetenv(s);
9826    }
9827    putenv_internal(s, l, core::ptr::null_mut())
9828}
9829
9830#[no_mangle]
9831pub unsafe extern "C" fn unsetenv(name: *const c_char) -> c_int {
9832    if name.is_null() {
9833        ERRNO!() = EINVAL;
9834        return -1;
9835    }
9836    let name = name as *const u8;
9837    let l = strchrnul_u8(name, b'=') as usize - name as usize;
9838    if l == 0 || *name.add(l) != 0 {
9839        ERRNO!() = EINVAL;
9840        return -1;
9841    }
9842    if __environ.is_null() {
9843        return 0;
9844    }
9845    let mut e = __environ;
9846    let mut eo = e;
9847    while !(*e).is_null() {
9848        if strncmp(name, *e as *const u8, l) == 0 && *(*e as *const u8).add(l) == b'=' {
9849            env_rm_add(*e, core::ptr::null_mut());
9850        } else {
9851            if eo != e {
9852                *eo = *e;
9853            }
9854            eo = eo.add(1);
9855        }
9856        e = e.add(1);
9857    }
9858    if eo != e {
9859        *eo = core::ptr::null_mut();
9860    }
9861    environ = __environ;
9862    0
9863}
9864
9865#[no_mangle]
9866pub unsafe extern "C" fn clearenv() -> c_int {
9867    let e = __environ;
9868    __environ = core::ptr::null_mut();
9869    environ = core::ptr::null_mut();
9870    if !e.is_null() {
9871        let mut p = e;
9872        while !(*p).is_null() {
9873            env_rm_add(*p, core::ptr::null_mut());
9874            p = p.add(1);
9875        }
9876    }
9877    0
9878}
9879
9880// ============================================================
9881// random / srandom / initstate / setstate (BSD lagged fibonacci)
9882// ponytail: single-threaded, no locking; add lock if concurrency matters
9883// ============================================================
9884
9885static mut RANDOM_INIT: [u32; 32] = [
9886    0x00000000,0x5851f42d,0xc0b18ccf,0xcbb5f646,
9887    0xc7033129,0x30705b04,0x20fd5db4,0x9a8b7f78,
9888    0x502959d8,0xab894868,0x6c0356a7,0x88cdb7ff,
9889    0xb477d43f,0x70a3a52b,0xa8e4baf1,0xfd8341fc,
9890    0x8ae16fd9,0x742d2f7a,0x0d1f0796,0x76035e09,
9891    0x40f7702c,0x6fa72ca5,0xaaa84157,0x58a0df74,
9892    0xc74a0364,0xae533cc4,0x04185faf,0x6de3b115,
9893    0x0cab8628,0xf043bfa4,0x398150e9,0x37521657,
9894];
9895
9896static mut RANDOM_N: i32 = 31;
9897static mut RANDOM_I: i32 = 3;
9898static mut RANDOM_J: i32 = 0;
9899static mut RANDOM_X: *mut u32 = unsafe { core::ptr::addr_of_mut!(RANDOM_INIT).cast::<u32>().add(1) };
9900
9901unsafe fn random_lcg31(x: u32) -> u32 {
9902    (1103515245u32.wrapping_mul(x).wrapping_add(12345)) & 0x7fffffff
9903}
9904
9905unsafe fn random_lcg64(x: u64) -> u64 {
9906    6364136223846793005u64.wrapping_mul(x).wrapping_add(1)
9907}
9908
9909unsafe fn random_savestate() -> *mut u8 {
9910    (*RANDOM_X.offset(-1)) = ((RANDOM_N as u32) << 16) | ((RANDOM_I as u32) << 8) | (RANDOM_J as u32);
9911    RANDOM_X.offset(-1) as *mut u8
9912}
9913
9914unsafe fn random_loadstate(state: *mut u32) {
9915    RANDOM_X = state.add(1);
9916    RANDOM_N = ((*RANDOM_X.offset(-1)) >> 16) as i32;
9917    RANDOM_I = (((*RANDOM_X.offset(-1)) >> 8) & 0xff) as i32;
9918    RANDOM_J = ((*RANDOM_X.offset(-1)) & 0xff) as i32;
9919}
9920
9921unsafe fn random_srandom_inner(seed: u32) {
9922    let mut s = seed as u64;
9923    if RANDOM_N == 0 {
9924        *RANDOM_X = s as u32;
9925        return;
9926    }
9927    RANDOM_I = if RANDOM_N == 31 || RANDOM_N == 7 { 3 } else { 1 };
9928    RANDOM_J = 0;
9929    for k in 0..RANDOM_N as usize {
9930        s = random_lcg64(s);
9931        *RANDOM_X.add(k) = (s >> 32) as u32;
9932    }
9933    *RANDOM_X |= 1;
9934}
9935
9936#[no_mangle]
9937pub unsafe extern "C" fn srandom(seed: c_uint) {
9938    random_srandom_inner(seed);
9939}
9940
9941#[no_mangle]
9942pub unsafe extern "C" fn initstate(seed: c_uint, state: *mut c_char, size: usize) -> *mut c_char {
9943    if size < 8 { return core::ptr::null_mut(); }
9944    let old = random_savestate();
9945    RANDOM_N = if size < 32 { 0 }
9946        else if size < 64 { 7 }
9947        else if size < 128 { 15 }
9948        else if size < 256 { 31 }
9949        else { 63 };
9950    RANDOM_X = (state as *mut u32).add(1);
9951    random_srandom_inner(seed);
9952    random_savestate();
9953    old as *mut c_char
9954}
9955
9956#[no_mangle]
9957pub unsafe extern "C" fn setstate(state: *mut c_char) -> *mut c_char {
9958    let old = random_savestate();
9959    random_loadstate(state as *mut u32);
9960    old as *mut c_char
9961}
9962
9963#[no_mangle]
9964pub unsafe extern "C" fn random() -> c_long {
9965    let k: c_long;
9966    if RANDOM_N == 0 {
9967        *RANDOM_X = random_lcg31(*RANDOM_X);
9968        k = *RANDOM_X as c_long;
9969    } else {
9970        *RANDOM_X.add(RANDOM_I as usize) = (*RANDOM_X.add(RANDOM_I as usize)).wrapping_add(*RANDOM_X.add(RANDOM_J as usize));
9971        k = (*RANDOM_X.add(RANDOM_I as usize) >> 1) as c_long;
9972        RANDOM_I += 1;
9973        if RANDOM_I == RANDOM_N { RANDOM_I = 0; }
9974        RANDOM_J += 1;
9975        if RANDOM_J == RANDOM_N { RANDOM_J = 0; }
9976    }
9977    k
9978}
9979
9980// ============================================================
9981// multibyte (UTF-8, musl state machine)
9982// ============================================================
9983
9984const SA: u8 = 0xC2;
9985const SB: u8 = 0xF4;
9986
9987const BITTAB: [u32; 51] = [
9988    0xC000_0002, 0xC000_0003, 0xC000_0004, 0xC000_0005, 0xC000_0006, 0xC000_0007,
9989    0xC000_0008, 0xC000_0009, 0xC000_000A, 0xC000_000B, 0xC000_000C, 0xC000_000D,
9990    0xC000_000E, 0xC000_000F, 0xC000_0010, 0xC000_0011, 0xC000_0012, 0xC000_0013,
9991    0xC000_0014, 0xC000_0015, 0xC000_0016, 0xC000_0017, 0xC000_0018, 0xC000_0019,
9992    0xC000_001A, 0xC000_001B, 0xC000_001C, 0xC000_001D, 0xC000_001E, 0xC000_001F,
9993    0xB300_0000, 0xC300_0001, 0xC300_0002, 0xC300_0003, 0xC300_0004, 0xC300_0005,
9994    0xC300_0006, 0xC300_0007, 0xC300_0008, 0xC300_0009, 0xC300_000A, 0xC300_000B,
9995    0xC300_000C, 0xD300_000D, 0xC300_000E, 0xC300_000F, 0xBB0C_0000, 0xC30C_0001,
9996    0xC30C_0002, 0xC30C_0003, 0xDB0C_0004,
9997];
9998
9999#[inline]
10000fn mb_oob(c: u32, b: u8) -> u32 {
10001    let b3 = (b >> 3) as u32;
10002    ((b3.wrapping_sub(0x10)) | (b3.wrapping_add((c as i32 >> 26) as u32))) & !7
10003}
10004
10005static mut MB_STATE: c_uint = 0;
10006
10007const MB_CUR_MAX_VAL_UTF8: usize = 4;
10008const MB_CUR_MAX_VAL_C: usize = 1;
10009
10010#[no_mangle]
10011pub extern "C" fn __ctype_get_mb_cur_max() -> usize {
10012    unsafe { if LOCALE_CTYPE_UTF8 { MB_CUR_MAX_VAL_UTF8 } else { MB_CUR_MAX_VAL_C } }
10013}
10014
10015#[no_mangle]
10016pub unsafe extern "C" fn mbrtowc(
10017    wc: *mut c_int,
10018    src: *const c_char,
10019    n: usize,
10020    st: *mut c_uint,
10021) -> usize {
10022    let s = src as *const u8;
10023    let n0 = n;
10024    let mut dummy: c_int = 0;
10025    let wc = if wc.is_null() { &mut dummy } else { &mut *wc };
10026    let st: &mut c_uint = if st.is_null() { &mut *(&raw mut MB_STATE) } else { &mut *st };
10027
10028    let mut c: u32 = *st;
10029    let mut s = s;
10030    let mut n = n;
10031
10032    if s.is_null() {
10033        if c != 0 { *st = 0; ERRNO!() = EILSEQ; return !0usize; }
10034        return 0;
10035    }
10036
10037    if n == 0 { return !1usize; }
10038
10039    if unsafe { !LOCALE_CTYPE_UTF8 } {
10040        let b = *s;
10041        *wc = b as c_int;
10042        return if b == 0 { 0 } else { 1 };
10043    }
10044
10045    if c == 0 {
10046        if *s < 0x80 {
10047            let r = if *s == 0 { 0 } else { 1 };
10048            *wc = *s as c_int;
10049            return r;
10050        }
10051        if (*s).wrapping_sub(SA) as u32 > (SB - SA) as u32 {
10052            *st = 0; ERRNO!() = EILSEQ; return !0usize;
10053        }
10054        c = BITTAB[*s as usize - SA as usize];
10055        s = s.add(1);
10056        n -= 1;
10057    }
10058
10059    if n > 0 {
10060        if mb_oob(c, *s) != 0 {
10061            *st = 0; ERRNO!() = EILSEQ; return !0usize;
10062        }
10063        loop {
10064            c = (c << 6) | (*s as u32).wrapping_sub(0x80);
10065            s = s.add(1);
10066            n -= 1;
10067            if c & (1u32 << 31) == 0 {
10068                *st = 0;
10069                *wc = c as c_int;
10070                return n0 - n;
10071            }
10072            if n == 0 { break; }
10073            if (*s).wrapping_sub(0x80) >= 0x40 {
10074                *st = 0; ERRNO!() = EILSEQ; return !0usize;
10075            }
10076        }
10077    }
10078
10079    *st = c;
10080    !1usize
10081}
10082
10083#[no_mangle]
10084pub unsafe extern "C" fn wcrtomb(
10085    s: *mut c_char,
10086    wc: c_int,
10087    _st: *mut c_uint,
10088) -> usize {
10089    if s.is_null() { return 1; }
10090    let wc_u = wc as u32;
10091    if unsafe { !LOCALE_CTYPE_UTF8 } {
10092        if wc_u > 0xFF {
10093            ERRNO!() = EILSEQ;
10094            return !0usize;
10095        }
10096        *s = wc as c_char;
10097        return 1;
10098    }
10099    if wc_u < 0x80 {
10100        *s = wc as c_char;
10101        return 1;
10102    }
10103    if wc_u < 0x800 {
10104        *s = (0xC0 | (wc_u >> 6)) as c_char;
10105        *s.add(1) = (0x80 | (wc_u & 0x3F)) as c_char;
10106        return 2;
10107    }
10108    if wc_u < 0xD800 || wc_u.wrapping_sub(0xE000) < 0x2000 {
10109        *s = (0xE0 | (wc_u >> 12)) as c_char;
10110        *s.add(1) = (0x80 | ((wc_u >> 6) & 0x3F)) as c_char;
10111        *s.add(2) = (0x80 | (wc_u & 0x3F)) as c_char;
10112        return 3;
10113    }
10114    if wc_u.wrapping_sub(0x10000) < 0x100000 {
10115        *s = (0xF0 | (wc_u >> 18)) as c_char;
10116        *s.add(1) = (0x80 | ((wc_u >> 12) & 0x3F)) as c_char;
10117        *s.add(2) = (0x80 | ((wc_u >> 6) & 0x3F)) as c_char;
10118        *s.add(3) = (0x80 | (wc_u & 0x3F)) as c_char;
10119        return 4;
10120    }
10121    ERRNO!() = EILSEQ;
10122    !0usize
10123}
10124
10125#[no_mangle]
10126pub unsafe extern "C" fn mblen(s: *const c_char, n: usize) -> c_int {
10127    if s.is_null() { return 0; }
10128    mbtowc(core::ptr::null_mut(), s, n)
10129}
10130
10131#[no_mangle]
10132pub unsafe extern "C" fn mbtowc(
10133    wc: *mut c_int,
10134    src: *const c_char,
10135    n: usize,
10136) -> c_int {
10137    if src.is_null() { return 0; }
10138    if n == 0 { ERRNO!() = EILSEQ; return -1; }
10139    let mut state: c_uint = 0;
10140    let mut w: c_int = 0;
10141    let r = mbrtowc(&mut w, src, n, &mut state);
10142    if r == !0usize { return -1; }
10143    if !wc.is_null() { *wc = w; }
10144    r as c_int
10145}
10146
10147#[no_mangle]
10148pub unsafe extern "C" fn wctomb(s: *mut c_char, wc: c_int) -> c_int {
10149    if s.is_null() { return 0; }
10150    wcrtomb(s, wc, core::ptr::null_mut()) as c_int
10151}
10152
10153#[no_mangle]
10154pub unsafe extern "C" fn mbsrtowcs(
10155    ws: *mut c_int,
10156    src: *mut *const c_char,
10157    wn: usize,
10158    st: *mut c_uint,
10159) -> usize {
10160    let st: &mut c_uint = if st.is_null() { &mut *(&raw mut MB_STATE) } else { &mut *st };
10161    let mut s = *src as *const u8;
10162    let wn0 = wn;
10163    let mut wn = wn;
10164
10165    if ws.is_null() {
10166        while *s != 0 {
10167            if *s < 0x80 {
10168                s = s.add(1);
10169                wn = wn.wrapping_sub(1);
10170                continue;
10171            }
10172            let mut wc: c_int = 0;
10173            let r = mbrtowc(&mut wc, s as *const c_char, 4, st);
10174            if r == !0usize { return !0usize; }
10175            if r == !1usize { return wn0 - wn; }
10176            s = s.add(r);
10177            wn = wn.wrapping_sub(1);
10178        }
10179        wn0 - wn
10180    } else {
10181        let mut w = ws;
10182        while wn > 0 {
10183            if *s < 0x80 {
10184                let c = *s as c_int;
10185                *w = c;
10186                if c == 0 {
10187                    *src = core::ptr::null();
10188                    return wn0 - wn;
10189                }
10190                w = w.add(1);
10191                s = s.add(1);
10192                wn -= 1;
10193                continue;
10194            }
10195            let mut wc: c_int = 0;
10196            let r = mbrtowc(&mut wc, s as *const c_char, 4, st);
10197            if r == !0usize {
10198                *src = s as *const c_char;
10199                return !0usize;
10200            }
10201            if r == !1usize {
10202                *src = s as *const c_char;
10203                return wn0 - wn;
10204            }
10205            *w = wc;
10206            w = w.add(1);
10207            s = s.add(r);
10208            wn -= 1;
10209        }
10210        *src = s as *const c_char;
10211        wn0 - wn
10212    }
10213}
10214
10215#[no_mangle]
10216pub unsafe extern "C" fn wcsrtombs(
10217    s: *mut c_char,
10218    ws: *mut *const c_int,
10219    n: usize,
10220    _st: *mut c_uint,
10221) -> usize {
10222    let mut w = *ws;
10223    let n0 = n;
10224    let mut n = n;
10225
10226    if s.is_null() {
10227        let mut count = 0usize;
10228        while *w != 0 {
10229            if (*w as u32) < 0x80 {
10230                count += 1;
10231            } else {
10232                let mut buf = [0u8; 4];
10233                let r = wcrtomb(buf.as_mut_ptr() as *mut c_char, *w, core::ptr::null_mut());
10234                if r == !0usize { return !0usize; }
10235                count += r;
10236            }
10237            w = w.add(1);
10238        }
10239        return count;
10240    }
10241
10242    let mut dst = s;
10243    while n > 0 {
10244        if (*w as u32) < 0x80 {
10245            *dst = *w as c_char;
10246            if *w == 0 {
10247                *ws = core::ptr::null();
10248                return n0 - n;
10249            }
10250            dst = dst.add(1);
10251            n -= 1;
10252        } else {
10253            let r = wcrtomb(dst, *w, core::ptr::null_mut());
10254            if r == !0usize {
10255                *ws = w;
10256                return !0usize;
10257            }
10258            if r > n {
10259                *ws = w;
10260                return n0 - n;
10261            }
10262            dst = dst.add(r);
10263            n -= r;
10264        }
10265        w = w.add(1);
10266    }
10267    *ws = w;
10268    n0
10269}
10270
10271#[no_mangle]
10272pub unsafe extern "C" fn mbstowcs(
10273    ws: *mut c_int,
10274    src: *const c_char,
10275    wn: usize,
10276) -> usize {
10277    let mut src_ptr = src;
10278    mbsrtowcs(ws, &mut src_ptr, wn, core::ptr::null_mut())
10279}
10280
10281#[no_mangle]
10282pub unsafe extern "C" fn wcstombs(
10283    s: *mut c_char,
10284    ws: *const c_int,
10285    n: usize,
10286) -> usize {
10287    let mut ws_ptr = ws;
10288    wcsrtombs(s, &mut ws_ptr, n, core::ptr::null_mut())
10289}
10290
10291// ============================================================
10292// wchar: btowc / wctob / mbsinit / mbrlen
10293// ============================================================
10294
10295#[no_mangle]
10296pub extern "C" fn btowc(c: c_int) -> wint_t {
10297    if c == -1 { return WEOF; }
10298    unsafe {
10299        if LOCALE_CTYPE_UTF8 {
10300            if (c as u32) < 128 { c as wint_t } else { WEOF }
10301        } else {
10302            (c as u8) as wint_t
10303        }
10304    }
10305}
10306
10307#[no_mangle]
10308pub extern "C" fn wctob(c: wint_t) -> c_int {
10309    unsafe {
10310        if LOCALE_CTYPE_UTF8 {
10311            if c < 128 { c as c_int } else { -1 }
10312        } else {
10313            if c <= 0xFF { c as c_int } else { -1 }
10314        }
10315    }
10316}
10317
10318#[no_mangle]
10319pub unsafe extern "C" fn mbsinit(s: *const c_uint) -> c_int {
10320    if s.is_null() || *s == 0 { 1 } else { 0 }
10321}
10322
10323#[no_mangle]
10324pub unsafe extern "C" fn mbrlen(s: *const c_char, n: usize, ps: *mut c_uint) -> usize {
10325    mbrtowc(core::ptr::null_mut(), s, n, ps)
10326}
10327
10328// ============================================================
10329// wchar: wide string functions
10330// ============================================================
10331
10332#[no_mangle]
10333pub unsafe extern "C" fn wcslen(s: *const wchar_t) -> usize {
10334    let mut len = 0;
10335    while *s.add(len) != 0 { len += 1; }
10336    len
10337}
10338
10339#[no_mangle]
10340pub unsafe extern "C" fn wcschr(s: *const wchar_t, c: wchar_t) -> *mut wchar_t {
10341    let mut i = 0;
10342    loop {
10343        let ch = *s.add(i);
10344        if ch == c { return s.add(i) as *mut wchar_t; }
10345        if ch == 0 { return null_mut(); }
10346        i += 1;
10347    }
10348}
10349
10350#[no_mangle]
10351pub unsafe extern "C" fn wcsrchr(s: *const wchar_t, c: wchar_t) -> *mut wchar_t {
10352    let mut i = 0;
10353    let mut last: *mut wchar_t = null_mut();
10354    loop {
10355        let ch = *s.add(i);
10356        if ch == c { last = s.add(i) as *mut wchar_t; }
10357        if ch == 0 { return last; }
10358        i += 1;
10359    }
10360}
10361
10362#[no_mangle]
10363pub unsafe extern "C" fn wcsstr(haystack: *const wchar_t, needle: *const wchar_t) -> *mut wchar_t {
10364    if *needle == 0 { return haystack as *mut wchar_t; }
10365    let mut i = 0;
10366    loop {
10367        let h = *haystack.add(i);
10368        if h == 0 { return null_mut(); }
10369        if h == *needle {
10370            let mut j = 0;
10371            loop {
10372                let n = *needle.add(j);
10373                if n == 0 { return haystack.add(i) as *mut wchar_t; }
10374                if *haystack.add(i + j) != n { break; }
10375                j += 1;
10376            }
10377        }
10378        i += 1;
10379    }
10380}
10381
10382#[no_mangle]
10383pub unsafe extern "C" fn wcscmp(s1: *const wchar_t, s2: *const wchar_t) -> c_int {
10384    let mut i = 0;
10385    loop {
10386        let a = *s1.add(i);
10387        let b = *s2.add(i);
10388        if a != b { return a - b; }
10389        if a == 0 { return 0; }
10390        i += 1;
10391    }
10392}
10393
10394#[no_mangle]
10395pub unsafe extern "C" fn wcsncmp(s1: *const wchar_t, s2: *const wchar_t, n: usize) -> c_int {
10396    let mut i = 0;
10397    while i < n {
10398        let a = *s1.add(i);
10399        let b = *s2.add(i);
10400        if a != b { return a - b; }
10401        if a == 0 { return 0; }
10402        i += 1;
10403    }
10404    0
10405}
10406
10407#[no_mangle]
10408pub unsafe extern "C" fn wcscpy(dst: *mut wchar_t, src: *const wchar_t) -> *mut wchar_t {
10409    let mut i = 0;
10410    loop {
10411        let c = *src.add(i);
10412        *dst.add(i) = c;
10413        if c == 0 { break; }
10414        i += 1;
10415    }
10416    dst
10417}
10418
10419#[no_mangle]
10420pub unsafe extern "C" fn wcsncpy(dst: *mut wchar_t, src: *const wchar_t, n: usize) -> *mut wchar_t {
10421    let mut i = 0;
10422    while i < n && *src.add(i) != 0 { *dst.add(i) = *src.add(i); i += 1; }
10423    while i < n { *dst.add(i) = 0; i += 1; }
10424    dst
10425}
10426
10427#[no_mangle]
10428pub unsafe extern "C" fn wcscat(dst: *mut wchar_t, src: *const wchar_t) -> *mut wchar_t {
10429    let mut i = 0;
10430    while *dst.add(i) != 0 { i += 1; }
10431    let mut j = 0;
10432    loop {
10433        let c = *src.add(j);
10434        *dst.add(i + j) = c;
10435        if c == 0 { break; }
10436        j += 1;
10437    }
10438    dst
10439}
10440
10441#[no_mangle]
10442pub unsafe extern "C" fn wcsncat(dst: *mut wchar_t, src: *const wchar_t, n: usize) -> *mut wchar_t {
10443    let mut i = 0;
10444    while *dst.add(i) != 0 { i += 1; }
10445    let mut j = 0;
10446    while j < n && *src.add(j) != 0 { *dst.add(i + j) = *src.add(j); j += 1; }
10447    *dst.add(i + j) = 0;
10448    dst
10449}
10450
10451#[no_mangle]
10452pub unsafe extern "C" fn wcsdup(s: *const wchar_t) -> *mut wchar_t {
10453    let len = wcslen(s);
10454    let p = malloc((len + 1) * core::mem::size_of::<wchar_t>()) as *mut wchar_t;
10455    if p.is_null() { return null_mut(); }
10456    core::ptr::copy_nonoverlapping(s, p, len + 1);
10457    p
10458}
10459
10460#[no_mangle]
10461pub unsafe extern "C" fn wcsnlen(s: *const wchar_t, maxlen: usize) -> usize {
10462    let mut i = 0;
10463    while i < maxlen && *s.add(i) != 0 { i += 1; }
10464    i
10465}
10466
10467#[no_mangle]
10468pub unsafe extern "C" fn wcsspn(s: *const wchar_t, accept: *const wchar_t) -> usize {
10469    let mut i = 0;
10470    loop {
10471        let c = *s.add(i);
10472        if c == 0 { return i; }
10473        let mut found = false;
10474        let mut j = 0;
10475        loop {
10476            let a = *accept.add(j);
10477            if a == 0 { break; }
10478            if c == a { found = true; break; }
10479            j += 1;
10480        }
10481        if !found { return i; }
10482        i += 1;
10483    }
10484}
10485
10486#[no_mangle]
10487pub unsafe extern "C" fn wcscspn(s: *const wchar_t, reject: *const wchar_t) -> usize {
10488    let mut i = 0;
10489    loop {
10490        let c = *s.add(i);
10491        if c == 0 { return i; }
10492        let mut j = 0;
10493        loop {
10494            let r = *reject.add(j);
10495            if r == 0 { break; }
10496            if c == r { return i; }
10497            j += 1;
10498        }
10499        i += 1;
10500    }
10501}
10502
10503#[no_mangle]
10504pub unsafe extern "C" fn wcspbrk(s: *const wchar_t, accept: *const wchar_t) -> *mut wchar_t {
10505    let mut i = 0;
10506    loop {
10507        let c = *s.add(i);
10508        if c == 0 { return null_mut(); }
10509        let mut j = 0;
10510        loop {
10511            let a = *accept.add(j);
10512            if a == 0 { break; }
10513            if c == a { return s.add(i) as *mut wchar_t; }
10514            j += 1;
10515        }
10516        i += 1;
10517    }
10518}
10519
10520#[no_mangle]
10521pub unsafe extern "C" fn wcsxfrm(dst: *mut wchar_t, src: *const wchar_t, n: usize) -> usize {
10522    let len = wcslen(src);
10523    if !dst.is_null() {
10524        let copy = if len < n { len } else { if n > 0 { n - 1 } else { 0 } };
10525        for i in 0..copy { *dst.add(i) = *src.add(i); }
10526        if n > 0 { *dst.add(copy) = 0; }
10527    }
10528    len
10529}
10530
10531// ============================================================
10532// wchar: wcsto* number conversions
10533// ============================================================
10534
10535unsafe fn wcs_to_bytes(ws: *const wchar_t, buf: *mut u8, bufsz: usize) -> usize {
10536    let mut i = 0;
10537    while i < bufsz - 1 {
10538        let c = *ws.add(i);
10539        if c == 0 { break; }
10540        if (c as u32) > 127 { break; }
10541        *buf.add(i) = c as u8;
10542        i += 1;
10543    }
10544    *buf.add(i) = 0;
10545    i
10546}
10547
10548#[no_mangle]
10549pub unsafe extern "C" fn wcstol(s: *const wchar_t, endptr: *mut *mut wchar_t, base: c_int) -> c_long {
10550    let mut buf = [0u8; 256];
10551    wcs_to_bytes(s, buf.as_mut_ptr(), 256);
10552    let mut end: *mut c_char = core::ptr::null_mut();
10553    let r = strtol(buf.as_ptr() as *const c_char, &mut end, base);
10554    if !endptr.is_null() {
10555        let offset = end as usize - buf.as_ptr() as usize;
10556        *endptr = s.add(offset) as *mut wchar_t;
10557    }
10558    r
10559}
10560
10561#[no_mangle]
10562pub unsafe extern "C" fn wcstoul(s: *const wchar_t, endptr: *mut *mut wchar_t, base: c_int) -> c_ulong {
10563    let mut buf = [0u8; 256];
10564    wcs_to_bytes(s, buf.as_mut_ptr(), 256);
10565    let mut end: *mut c_char = core::ptr::null_mut();
10566    let r = strtoul(buf.as_ptr() as *const c_char, &mut end, base);
10567    if !endptr.is_null() {
10568        let offset = end as usize - buf.as_ptr() as usize;
10569        *endptr = s.add(offset) as *mut wchar_t;
10570    }
10571    r
10572}
10573
10574#[no_mangle]
10575pub unsafe extern "C" fn wcstoll(s: *const wchar_t, endptr: *mut *mut wchar_t, base: c_int) -> c_longlong {
10576    let mut buf = [0u8; 256];
10577    wcs_to_bytes(s, buf.as_mut_ptr(), 256);
10578    let mut end: *mut c_char = core::ptr::null_mut();
10579    let r = strtoll(buf.as_ptr() as *const c_char, &mut end, base);
10580    if !endptr.is_null() {
10581        let offset = end as usize - buf.as_ptr() as usize;
10582        *endptr = s.add(offset) as *mut wchar_t;
10583    }
10584    r
10585}
10586
10587#[no_mangle]
10588pub unsafe extern "C" fn wcstoull(s: *const wchar_t, endptr: *mut *mut wchar_t, base: c_int) -> c_ulonglong {
10589    let mut buf = [0u8; 256];
10590    wcs_to_bytes(s, buf.as_mut_ptr(), 256);
10591    let mut end: *mut c_char = core::ptr::null_mut();
10592    let r = strtoull(buf.as_ptr() as *const c_char, &mut end, base);
10593    if !endptr.is_null() {
10594        let offset = end as usize - buf.as_ptr() as usize;
10595        *endptr = s.add(offset) as *mut wchar_t;
10596    }
10597    r
10598}
10599
10600#[no_mangle]
10601pub unsafe extern "C" fn wcstod(s: *const wchar_t, endptr: *mut *mut wchar_t) -> f64 {
10602    let mut buf = [0u8; 256];
10603    wcs_to_bytes(s, buf.as_mut_ptr(), 256);
10604    let mut end: *mut c_char = core::ptr::null_mut();
10605    let r = strtod(buf.as_ptr() as *const c_char, &mut end);
10606    if !endptr.is_null() {
10607        let offset = end as usize - buf.as_ptr() as usize;
10608        *endptr = s.add(offset) as *mut wchar_t;
10609    }
10610    r
10611}
10612
10613#[no_mangle]
10614pub unsafe extern "C" fn wcstof(s: *const wchar_t, endptr: *mut *mut wchar_t) -> f32 { wcstod(s, endptr) as f32 }
10615
10616#[no_mangle]
10617#[cfg(target_arch = "x86_64")]
10618pub unsafe extern "C" fn wcstold(s: *const wchar_t, endptr: *mut *mut wchar_t) -> f64 { wcstod(s, endptr) }
10619
10620#[no_mangle]
10621#[cfg(target_arch = "aarch64")]
10622pub unsafe extern "C" fn wcstold(s: *const wchar_t, endptr: *mut *mut wchar_t) -> f128 { wcstod(s, endptr) as f128 }
10623
10624#[no_mangle]
10625#[cfg(target_arch = "riscv64")]
10626pub unsafe extern "C" fn wcstold(s: *const wchar_t, endptr: *mut *mut wchar_t) -> f128 { wcstod(s, endptr) as f128 }
10627
10628#[no_mangle]
10629pub unsafe extern "C" fn wcstoimax(s: *const wchar_t, endptr: *mut *mut wchar_t, base: c_int) -> c_longlong { wcstoll(s, endptr, base) }
10630
10631#[no_mangle]
10632pub unsafe extern "C" fn wcstoumax(s: *const wchar_t, endptr: *mut *mut wchar_t, base: c_int) -> c_ulonglong { wcstoull(s, endptr, base) }
10633
10634// ============================================================
10635// wctype: classification functions
10636// ============================================================
10637
10638const WC_ALPHA: c_int = 1;
10639const WC_DIGIT: c_int = 2;
10640const WC_SPACE: c_int = 4;
10641const WC_UPPER: c_int = 8;
10642const WC_LOWER: c_int = 16;
10643const WC_ALNUM: c_int = WC_ALPHA | WC_DIGIT;
10644const WC_BLANK: c_int = 32;
10645const WC_CNTRL: c_int = 64;
10646const WC_PUNCT: c_int = 128;
10647const WC_GRAPH: c_int = WC_ALNUM | WC_PUNCT;
10648const WC_PRINT: c_int = WC_GRAPH | WC_BLANK;
10649const WC_XDIGIT: c_int = 256;
10650
10651fn wc_flags(c: wint_t) -> c_int {
10652    if c < 128 {
10653        let f = CT_TABLE[c as usize];
10654        let mut r = 0;
10655        if f & (CT_UPPER | CT_LOWER) != 0 { r |= WC_ALPHA; }
10656        if f & CT_DIGIT != 0 { r |= WC_DIGIT; }
10657        if f & CT_SPACE != 0 { r |= WC_SPACE; }
10658        if f & CT_UPPER != 0 { r |= WC_UPPER; }
10659        if f & CT_LOWER != 0 { r |= WC_LOWER; }
10660        if f & CT_BLANK != 0 { r |= WC_BLANK; }
10661        if f & CT_CNTRL != 0 { r |= WC_CNTRL; }
10662        if f & CT_PUNCT != 0 { r |= WC_PUNCT; }
10663        if f & CT_XDIGIT != 0 { r |= WC_XDIGIT; }
10664        r
10665    } else {
10666        if c < 0x100 { 0 }
10667        else { WC_PRINT | WC_ALPHA }
10668    }
10669}
10670
10671#[no_mangle]
10672pub extern "C" fn iswalnum(c: wint_t) -> c_int { (wc_flags(c) & WC_ALNUM != 0) as c_int }
10673#[no_mangle]
10674pub extern "C" fn iswalpha(c: wint_t) -> c_int { (wc_flags(c) & WC_ALPHA != 0) as c_int }
10675#[no_mangle]
10676pub extern "C" fn iswblank(c: wint_t) -> c_int { (wc_flags(c) & WC_BLANK != 0) as c_int }
10677#[no_mangle]
10678pub extern "C" fn iswcntrl(c: wint_t) -> c_int { (wc_flags(c) & WC_CNTRL != 0) as c_int }
10679#[no_mangle]
10680pub extern "C" fn iswdigit(c: wint_t) -> c_int { ((c >= b'0' as wint_t) && (c <= b'9' as wint_t)) as c_int }
10681#[no_mangle]
10682pub extern "C" fn iswgraph(c: wint_t) -> c_int { (wc_flags(c) & WC_GRAPH != 0) as c_int }
10683#[no_mangle]
10684pub extern "C" fn iswlower(c: wint_t) -> c_int { (wc_flags(c) & WC_LOWER != 0) as c_int }
10685#[no_mangle]
10686pub extern "C" fn iswprint(c: wint_t) -> c_int {
10687    if c < 128 { (wc_flags(c) & WC_PRINT != 0) as c_int }
10688    else if c >= 0x100 && c < 0x110000 { 1 }
10689    else { 0 }
10690}
10691#[no_mangle]
10692pub extern "C" fn iswpunct(c: wint_t) -> c_int { (wc_flags(c) & WC_PUNCT != 0) as c_int }
10693#[no_mangle]
10694pub extern "C" fn iswspace(c: wint_t) -> c_int {
10695    if c < 128 { (wc_flags(c) & WC_SPACE != 0) as c_int }
10696    else {
10697        match c {
10698            0x1680 | 0x2000..=0x200A | 0x2028 | 0x2029 | 0x202F | 0x205F | 0x3000 => 1,
10699            _ => 0,
10700        }
10701    }
10702}
10703#[no_mangle]
10704pub extern "C" fn iswupper(c: wint_t) -> c_int { (wc_flags(c) & WC_UPPER != 0) as c_int }
10705#[no_mangle]
10706pub extern "C" fn iswxdigit(c: wint_t) -> c_int { (wc_flags(c) & WC_XDIGIT != 0) as c_int }
10707
10708static WCTYPE_ALNUM: c_int = WC_ALNUM;
10709static WCTYPE_ALPHA: c_int = WC_ALPHA;
10710static WCTYPE_BLANK: c_int = WC_BLANK;
10711static WCTYPE_CNTRL: c_int = WC_CNTRL;
10712static WCTYPE_DIGIT: c_int = WC_DIGIT;
10713static WCTYPE_GRAPH: c_int = WC_GRAPH;
10714static WCTYPE_LOWER: c_int = WC_LOWER;
10715static WCTYPE_PRINT: c_int = WC_PRINT;
10716static WCTYPE_PUNCT: c_int = WC_PUNCT;
10717static WCTYPE_SPACE: c_int = WC_SPACE;
10718static WCTYPE_UPPER: c_int = WC_UPPER;
10719static WCTYPE_XDIGIT: c_int = WC_XDIGIT;
10720
10721#[no_mangle]
10722pub unsafe extern "C" fn wctype(name: *const c_char) -> wctype_t {
10723    if name.is_null() { return core::ptr::null(); }
10724    let n = name as *const u8;
10725    if strcmp(n, b"alnum\0".as_ptr()) == 0 { return &WCTYPE_ALNUM; }
10726    if strcmp(n, b"alpha\0".as_ptr()) == 0 { return &WCTYPE_ALPHA; }
10727    if strcmp(n, b"blank\0".as_ptr()) == 0 { return &WCTYPE_BLANK; }
10728    if strcmp(n, b"cntrl\0".as_ptr()) == 0 { return &WCTYPE_CNTRL; }
10729    if strcmp(n, b"digit\0".as_ptr()) == 0 { return &WCTYPE_DIGIT; }
10730    if strcmp(n, b"graph\0".as_ptr()) == 0 { return &WCTYPE_GRAPH; }
10731    if strcmp(n, b"lower\0".as_ptr()) == 0 { return &WCTYPE_LOWER; }
10732    if strcmp(n, b"print\0".as_ptr()) == 0 { return &WCTYPE_PRINT; }
10733    if strcmp(n, b"punct\0".as_ptr()) == 0 { return &WCTYPE_PUNCT; }
10734    if strcmp(n, b"space\0".as_ptr()) == 0 { return &WCTYPE_SPACE; }
10735    if strcmp(n, b"upper\0".as_ptr()) == 0 { return &WCTYPE_UPPER; }
10736    if strcmp(n, b"xdigit\0".as_ptr()) == 0 { return &WCTYPE_XDIGIT; }
10737    core::ptr::null()
10738}
10739
10740#[no_mangle]
10741pub unsafe extern "C" fn iswctype(c: wint_t, desc: wctype_t) -> c_int {
10742    if desc.is_null() { return 0; }
10743    (wc_flags(c) & *desc != 0) as c_int
10744}
10745
10746static WCTRANS_LOWER: c_int = 1;
10747static WCTRANS_UPPER: c_int = 2;
10748
10749#[no_mangle]
10750pub unsafe extern "C" fn towlower(c: wint_t) -> wint_t {
10751    if c < 128 { tolower(c as c_int) as wint_t } else { c }
10752}
10753
10754#[no_mangle]
10755pub unsafe extern "C" fn towupper(c: wint_t) -> wint_t {
10756    if c < 128 { toupper(c as c_int) as wint_t } else { c }
10757}
10758
10759#[no_mangle]
10760pub unsafe extern "C" fn wctrans(name: *const c_char) -> wctrans_t {
10761    if name.is_null() { return core::ptr::null(); }
10762    let n = name as *const u8;
10763    if strcmp(n, b"tolower\0".as_ptr()) == 0 { return &WCTRANS_LOWER; }
10764    if strcmp(n, b"toupper\0".as_ptr()) == 0 { return &WCTRANS_UPPER; }
10765    core::ptr::null()
10766}
10767
10768#[no_mangle]
10769pub unsafe extern "C" fn towctrans(c: wint_t, desc: wctrans_t) -> wint_t {
10770    if desc.is_null() { return c; }
10771    match *desc {
10772        1 => towlower(c),
10773        2 => towupper(c),
10774        _ => c,
10775    }
10776}
10777
10778// ============================================================
10779// wchar: wide stdio
10780// ============================================================
10781
10782#[no_mangle]
10783pub unsafe extern "C" fn fgetwc(f: *mut FILE) -> wint_t {
10784    let mut state: c_uint = 0;
10785    let mut wc: wchar_t = 0;
10786    let mut buf = [0u8; 4];
10787    let c = fgetc(f);
10788    if c == -1 { return WEOF; }
10789    buf[0] = c as u8;
10790    if buf[0] < 0x80 { return buf[0] as wint_t; }
10791    let len = if buf[0] < 0xE0 { 2 } else if buf[0] < 0xF0 { 3 } else { 4 };
10792    let mut n = 1usize;
10793    while n < len {
10794        let c2 = fgetc(f);
10795        if c2 == -1 { return WEOF; }
10796        buf[n] = c2 as u8;
10797        n += 1;
10798    }
10799    let r = mbrtowc(&mut wc, buf.as_ptr() as *const c_char, n, &mut state);
10800    if r == !0usize || r == !1usize { WEOF } else { wc as wint_t }
10801}
10802
10803#[no_mangle]
10804pub unsafe extern "C" fn fputwc(c: wchar_t, f: *mut FILE) -> wint_t {
10805    let mut buf = [0u8; 4];
10806    let n = wcrtomb(buf.as_mut_ptr() as *mut c_char, c, core::ptr::null_mut());
10807    if n == !0usize || n == 0 { return WEOF; }
10808    if fwrite(buf.as_ptr() as *const c_void, 1, n, f) != n { return WEOF; }
10809    c as wint_t
10810}
10811
10812#[no_mangle]
10813pub unsafe extern "C" fn fputws(s: *const wchar_t, f: *mut FILE) -> c_int {
10814    let mut i = 0;
10815    while *s.add(i) != 0 {
10816        if fputwc(*s.add(i), f) == WEOF { return -1; }
10817        i += 1;
10818    }
10819    0
10820}
10821
10822#[no_mangle]
10823pub unsafe extern "C" fn ungetwc(c: wint_t, f: *mut FILE) -> wint_t {
10824    if c == WEOF { return WEOF; }
10825    let mut buf = [0u8; 4];
10826    let n = wcrtomb(buf.as_mut_ptr() as *mut c_char, c as wchar_t, core::ptr::null_mut());
10827    if n == !0usize || n == 0 { return WEOF; }
10828    let mut i = n;
10829    while i > 0 {
10830        i -= 1;
10831        if ungetc(buf[i] as c_int, f) == -1 { return WEOF; }
10832    }
10833    c
10834}
10835
10836#[no_mangle]
10837pub unsafe extern "C" fn getwchar() -> wint_t { fgetwc(stdin) }
10838
10839#[no_mangle]
10840pub unsafe extern "C" fn putwchar(c: wchar_t) -> wint_t { fputwc(c, stdout) }
10841
10842// ============================================================
10843// wchar: swprintf / vswprintf / fwprintf / vfwprintf
10844// ============================================================
10845
10846unsafe fn wfmt_write_str(dst: *mut wchar_t, pos: usize, cap: usize, s: *const u8, len: usize) -> usize {
10847    let mut i = 0;
10848    while i < len {
10849        if pos + i < cap { *dst.add(pos + i) = *s.add(i) as wchar_t; }
10850        i += 1;
10851    }
10852    pos + len
10853}
10854
10855#[no_mangle]
10856pub unsafe extern "C" fn vswprintf(s: *mut wchar_t, n: usize, fmt: *const wchar_t, mut args: VaList) -> c_int {
10857    if n == 0 { return -1; }
10858    let cap = n - 1;
10859    let mut pos = 0usize;
10860    // ponytail: format specifiers are ASCII; narrow buffer suffices for parsing
10861    let mut fmt_narrow = [0u8; 256];
10862    let mut flen = 0usize;
10863    loop {
10864        let ch = *fmt.add(flen) as u32;
10865        if ch == 0 || flen >= 255 { break; }
10866        fmt_narrow[flen] = if ch < 128 { ch as u8 } else { b'?' };
10867        flen += 1;
10868    }
10869    fmt_narrow[flen] = 0;
10870    let mut fi = 0usize;
10871    macro_rules! wc {
10872        ($c:expr) => { if pos < cap { *s.add(pos) = $c as wchar_t; } pos += 1; }
10873    }
10874    loop {
10875        let c = fmt_narrow[fi];
10876        if c == 0 { break; }
10877        if c != b'%' { wc!(c); fi += 1; continue; }
10878        fi += 1;
10879        let mut flags: u8 = 0;
10880        loop {
10881            match fmt_narrow[fi] {
10882                b'-' => { flags |= FLAG_MINUS; fi += 1; }
10883                b'+' => { flags |= FLAG_PLUS; fi += 1; }
10884                b' ' => { flags |= FLAG_SPACE; fi += 1; }
10885                b'0' => { flags |= FLAG_ZERO; fi += 1; }
10886                b'#' => { flags |= FLAG_HASH; fi += 1; }
10887                _ => break,
10888            }
10889        }
10890        let mut width: usize = 0;
10891        if fmt_narrow[fi] == b'*' {
10892            let w = args.next_arg::<c_int>();
10893            if w < 0 { flags |= FLAG_MINUS; width = (-w) as usize; } else { width = w as usize; }
10894            fi += 1;
10895        } else {
10896            while fmt_narrow[fi] >= b'0' && fmt_narrow[fi] <= b'9' {
10897                width = width * 10 + (fmt_narrow[fi] - b'0') as usize; fi += 1;
10898            }
10899        }
10900        let mut precision: i32 = -1;
10901        if fmt_narrow[fi] == b'.' {
10902            fi += 1;
10903            if fmt_narrow[fi] == b'*' {
10904                let p = args.next_arg::<c_int>();
10905                precision = if p < 0 { -1 } else { p }; fi += 1;
10906            } else {
10907                precision = 0;
10908                while fmt_narrow[fi] >= b'0' && fmt_narrow[fi] <= b'9' {
10909                    precision = precision * 10 + (fmt_narrow[fi] - b'0') as i32; fi += 1;
10910                }
10911            }
10912        }
10913        let len_mod = fmt_narrow[fi];
10914        if len_mod == b'h' || len_mod == b'l' || len_mod == b'j' || len_mod == b'z' || len_mod == b't' || len_mod == b'L' {
10915            fi += 1;
10916            let spec = fmt_narrow[fi];
10917            if (len_mod == b'h' && spec == b'h') || (len_mod == b'l' && spec == b'l') {
10918                fi += 1;
10919                let spec2 = fmt_narrow[fi];
10920                match (len_mod, spec, spec2) {
10921                    (b'h', b'h', b'n') => { let p = args.next_arg::<*mut c_void>(); if !p.is_null() { *(p as *mut u8) = pos as u8; } }
10922                    (b'l', b'l', b'n') => { let p = args.next_arg::<*mut c_void>(); if !p.is_null() { *(p as *mut c_longlong) = pos as c_longlong; } }
10923                    (b'l', b'l', b'd') | (b'l', b'l', b'i') => {
10924                        let val = args.next_arg::<c_longlong>();
10925                        let neg = val < 0;
10926                        let abs = if neg { val.wrapping_neg() as u64 } else { val as u64 };
10927                        let b = format_u64(abs);
10928                        let sign = if neg { Some(b'-') } else if flags & FLAG_PLUS != 0 { Some(b'+') } else if flags & FLAG_SPACE != 0 { Some(b' ') } else { None };
10929                        let mut fbuf = [0u8; 32];
10930                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, sign, None, precision, width, flags, false);
10931                        for k in 0..len { wc!(fbuf[k]); }
10932                    }
10933                    (b'l', b'l', b'u') => {
10934                        let val = args.next_arg::<c_ulonglong>();
10935                        let b = format_u64(val as u64);
10936                        let mut fbuf = [0u8; 32];
10937                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, None, precision, width, flags, false);
10938                        for k in 0..len { wc!(fbuf[k]); }
10939                    }
10940                    (b'l', b'l', b'x') => {
10941                        let val = args.next_arg::<c_ulonglong>();
10942                        let b = format_hex(val as u64, false);
10943                        let prefix = if flags & FLAG_HASH != 0 && !(b.1 == 1 && b.0[0] == b'0') { Some(b'x') } else { None };
10944                        let mut fbuf = [0u8; 32];
10945                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, prefix, precision, width, flags, false);
10946                        for k in 0..len { wc!(fbuf[k]); }
10947                    }
10948                    (b'l', b'l', b'X') => {
10949                        let val = args.next_arg::<c_ulonglong>();
10950                        let b = format_hex(val as u64, true);
10951                        let prefix = if flags & FLAG_HASH != 0 && !(b.1 == 1 && b.0[0] == b'0') { Some(b'X') } else { None };
10952                        let mut fbuf = [0u8; 32];
10953                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, prefix, precision, width, flags, false);
10954                        for k in 0..len { wc!(fbuf[k]); }
10955                    }
10956                    (b'l', b'l', b'o') => {
10957                        let val = args.next_arg::<c_ulonglong>();
10958                        let b = format_octal(val as u64, flags & FLAG_HASH != 0);
10959                        let mut fbuf = [0u8; 32];
10960                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, None, precision, width, flags, flags & FLAG_HASH != 0);
10961                        for k in 0..len { wc!(fbuf[k]); }
10962                    }
10963                    _ => { wc!(b'%'); wc!(len_mod); wc!(spec); wc!(spec2); }
10964                }
10965            } else {
10966                let lm = if (len_mod == b'z' || len_mod == b't') && spec != b'n' { b'l' } else { len_mod };
10967                match (lm, spec) {
10968                    (b'h', b'n') => { let p = args.next_arg::<*mut c_void>(); if !p.is_null() { *(p as *mut u16) = pos as u16; } }
10969                    (b'l', b'n') => { let p = args.next_arg::<*mut c_void>(); if !p.is_null() { *(p as *mut c_long) = pos as c_long; } }
10970                    (b'z', b'n') | (b't', b'n') => { let p = args.next_arg::<*mut c_void>(); if !p.is_null() { *(p as *mut usize) = pos; } }
10971                    (b'j', b'n') => { let p = args.next_arg::<*mut c_void>(); if !p.is_null() { *(p as *mut c_ulonglong) = pos as c_ulonglong; } }
10972                    (b'l', b'd') | (b'l', b'i') => {
10973                        let val = args.next_arg::<c_long>();
10974                        let neg = val < 0;
10975                        let abs = if neg { val.wrapping_neg() as u64 } else { val as u64 };
10976                        let b = format_u64(abs);
10977                        let sign = if neg { Some(b'-') } else if flags & FLAG_PLUS != 0 { Some(b'+') } else if flags & FLAG_SPACE != 0 { Some(b' ') } else { None };
10978                        let mut fbuf = [0u8; 32];
10979                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, sign, None, precision, width, flags, false);
10980                        for k in 0..len { wc!(fbuf[k]); }
10981                    }
10982                    (b'l', b'u') => {
10983                        let val = args.next_arg::<c_ulong>();
10984                        let b = format_u64(val as u64);
10985                        let mut fbuf = [0u8; 32];
10986                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, None, precision, width, flags, false);
10987                        for k in 0..len { wc!(fbuf[k]); }
10988                    }
10989                    (b'l', b'x') => {
10990                        let val = args.next_arg::<c_ulong>();
10991                        let b = format_hex(val as u64, false);
10992                        let prefix = if flags & FLAG_HASH != 0 && !(b.1 == 1 && b.0[0] == b'0') { Some(b'x') } else { None };
10993                        let mut fbuf = [0u8; 32];
10994                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, prefix, precision, width, flags, false);
10995                        for k in 0..len { wc!(fbuf[k]); }
10996                    }
10997                    (b'l', b'X') => {
10998                        let val = args.next_arg::<c_ulong>();
10999                        let b = format_hex(val as u64, true);
11000                        let prefix = if flags & FLAG_HASH != 0 && !(b.1 == 1 && b.0[0] == b'0') { Some(b'X') } else { None };
11001                        let mut fbuf = [0u8; 32];
11002                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, prefix, precision, width, flags, false);
11003                        for k in 0..len { wc!(fbuf[k]); }
11004                    }
11005                    (b'l', b'o') => {
11006                        let val = args.next_arg::<c_ulong>();
11007                        let b = format_octal(val as u64, flags & FLAG_HASH != 0);
11008                        let mut fbuf = [0u8; 32];
11009                        let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, None, precision, width, flags, flags & FLAG_HASH != 0);
11010                        for k in 0..len { wc!(fbuf[k]); }
11011                    }
11012                    (b'l', b'c') => {
11013                        let ch = args.next_arg::<wint_t>();
11014                        wc!(ch);
11015                    }
11016                    (b'l', b's') => {
11017                        let ws_arg = args.next_arg::<*const wchar_t>();
11018                        if ws_arg.is_null() {
11019                            let slen = if precision >= 0 { (precision as usize).min(6) } else { 6 };
11020                            if flags & FLAG_MINUS != 0 {
11021                                for k in 0..slen { wc!(b"(null)"[k]); }
11022                                for _ in 0..width.saturating_sub(slen) { wc!(b' '); }
11023                            } else {
11024                                for _ in 0..width.saturating_sub(slen) { wc!(b' '); }
11025                                for k in 0..slen { wc!(b"(null)"[k]); }
11026                            }
11027                        } else {
11028                            let mut wlen = 0usize;
11029                            loop {
11030                                let ch = *ws_arg.add(wlen);
11031                                if ch == 0 { break; }
11032                                if precision >= 0 && wlen >= precision as usize { break; }
11033                                wlen += 1;
11034                            }
11035                            if flags & FLAG_MINUS != 0 {
11036                                for k in 0..wlen { wc!(*ws_arg.add(k)); }
11037                                for _ in 0..width.saturating_sub(wlen) { wc!(b' '); }
11038                            } else {
11039                                for _ in 0..width.saturating_sub(wlen) { wc!(b' '); }
11040                                for k in 0..wlen { wc!(*ws_arg.add(k)); }
11041                            }
11042                        }
11043                    }
11044                    (b'l', b'f') | (b'l', b'F') | (b'l', b'e') | (b'l', b'E')
11045                    | (b'l', b'g') | (b'l', b'G') | (b'l', b'a') | (b'l', b'A') => {
11046                        let val = args.next_arg::<f64>();
11047                        let ucase = spec == b'F' || spec == b'E' || spec == b'G' || spec == b'A';
11048                        let ftype = match spec | 0x20 { b'f' => FMT_F, b'e' => FMT_E, b'g' => FMT_G, b'a' => FMT_A, _ => FMT_F };
11049                        let mut fbuf = [0u8; 4224];
11050                        let flen = format_f64_full(fbuf.as_mut_ptr(), val, ftype, precision, flags, ucase);
11051                        let mut wbuf = [0u8; 4224];
11052                        let wlen = apply_width_flags(wbuf.as_mut_ptr(), fbuf.as_ptr(), flen, width, flags);
11053                        for k in 0..wlen { wc!(wbuf[k]); }
11054                    }
11055                    (b'L', b'f') | (b'L', b'F') | (b'L', b'e') | (b'L', b'E')
11056                    | (b'L', b'g') | (b'L', b'G') | (b'L', b'a') | (b'L', b'A') => {
11057                        #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
11058                        let val = {
11059                            let lo: u64 = args.next_arg::<u64>();
11060                            let hi: u64 = args.next_arg::<u64>();
11061                            let combined: u128 = ((hi as u128) << 64) | (lo as u128);
11062                            f128::from_bits(combined) as f64
11063                        };
11064                        #[cfg(target_arch = "x86_64")]
11065                        let val = args.next_arg::<f64>();
11066                        let ucase = spec == b'F' || spec == b'E' || spec == b'G' || spec == b'A';
11067                        let ftype = match spec | 0x20 { b'f' => FMT_F, b'e' => FMT_E, b'g' => FMT_G, b'a' => FMT_A, _ => FMT_F };
11068                        let mut fbuf = [0u8; 4224];
11069                        let flen = format_f64_full(fbuf.as_mut_ptr(), val, ftype, precision, flags, ucase);
11070                        let mut wbuf = [0u8; 4224];
11071                        let wlen = apply_width_flags(wbuf.as_mut_ptr(), fbuf.as_ptr(), flen, width, flags);
11072                        for k in 0..wlen { wc!(wbuf[k]); }
11073                    }
11074                    _ => { wc!(b'%'); wc!(len_mod); wc!(spec); }
11075                }
11076            }
11077        } else {
11078            let spec = fmt_narrow[fi];
11079            match spec {
11080                b'd' | b'i' => {
11081                    let d = args.next_arg::<c_int>();
11082                    let neg = d < 0;
11083                    let abs = if neg { d.wrapping_neg() as u64 } else { d as u64 };
11084                    let b = format_u64(abs);
11085                    let sign = if neg { Some(b'-') } else if flags & FLAG_PLUS != 0 { Some(b'+') } else if flags & FLAG_SPACE != 0 { Some(b' ') } else { None };
11086                    let mut fbuf = [0u8; 32];
11087                    let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, sign, None, precision, width, flags, false);
11088                    for k in 0..len { wc!(fbuf[k]); }
11089                }
11090                b'u' => {
11091                    let u = args.next_arg::<c_uint>();
11092                    let b = format_u64(u as u64);
11093                    let mut fbuf = [0u8; 32];
11094                    let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, None, precision, width, flags, false);
11095                    for k in 0..len { wc!(fbuf[k]); }
11096                }
11097                b'x' => {
11098                    let x = args.next_arg::<c_uint>();
11099                    let b = format_hex(x as u64, false);
11100                    let prefix = if flags & FLAG_HASH != 0 && !(b.1 == 1 && b.0[0] == b'0') { Some(b'x') } else { None };
11101                    let mut fbuf = [0u8; 32];
11102                    let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, prefix, precision, width, flags, false);
11103                    for k in 0..len { wc!(fbuf[k]); }
11104                }
11105                b'X' => {
11106                    let x = args.next_arg::<c_uint>();
11107                    let b = format_hex(x as u64, true);
11108                    let prefix = if flags & FLAG_HASH != 0 && !(b.1 == 1 && b.0[0] == b'0') { Some(b'X') } else { None };
11109                    let mut fbuf = [0u8; 32];
11110                    let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, prefix, precision, width, flags, false);
11111                    for k in 0..len { wc!(fbuf[k]); }
11112                }
11113                b'o' => {
11114                    let o = args.next_arg::<c_uint>();
11115                    let b = format_octal(o as u64, flags & FLAG_HASH != 0);
11116                    let mut fbuf = [0u8; 32];
11117                    let len = format_int(fbuf.as_mut_ptr(), b.0.as_ptr(), b.1, None, None, precision, width, flags, flags & FLAG_HASH != 0);
11118                    for k in 0..len { wc!(fbuf[k]); }
11119                }
11120                b'f' | b'F' | b'e' | b'E' | b'g' | b'G' | b'a' | b'A' => {
11121                    let val = args.next_arg::<f64>();
11122                    let ucase = spec == b'F' || spec == b'E' || spec == b'G' || spec == b'A';
11123                    let ftype = match spec | 0x20 { b'f' => FMT_F, b'e' => FMT_E, b'g' => FMT_G, b'a' => FMT_A, _ => FMT_F };
11124                    let mut fbuf = [0u8; 4224];
11125                    let flen = format_f64_full(fbuf.as_mut_ptr(), val, ftype, precision, flags, ucase);
11126                    let mut wbuf = [0u8; 4224];
11127                    let wlen = apply_width_flags(wbuf.as_mut_ptr(), fbuf.as_ptr(), flen, width, flags);
11128                    for k in 0..wlen { wc!(wbuf[k]); }
11129                }
11130                b'c' => {
11131                    let ch = args.next_arg::<c_int>();
11132                    wc!(ch as u8);
11133                }
11134                b's' => {
11135                    let p = args.next_arg::<*const c_char>();
11136                    if p.is_null() {
11137                        let slen = if precision >= 0 { (precision as usize).min(6) } else { 6 };
11138                        if flags & FLAG_MINUS != 0 {
11139                            for k in 0..slen { wc!(b"(null)"[k]); }
11140                            for _ in 0..width.saturating_sub(slen) { wc!(b' '); }
11141                        } else {
11142                            for _ in 0..width.saturating_sub(slen) { wc!(b' '); }
11143                            for k in 0..slen { wc!(b"(null)"[k]); }
11144                        }
11145                    } else {
11146                        let mut wbuf_local = [0i32; 256];
11147                        let mut wcount = 0usize;
11148                        let mut byte_pos = 0usize;
11149                        let pbytes = p as *const u8;
11150                        loop {
11151                            if precision >= 0 && wcount >= precision as usize { break; }
11152                            if wcount >= 256 { break; }
11153                            let mut wc_val: c_int = 0;
11154                            let consumed = mbrtowc(&mut wc_val as *mut c_int, p.add(byte_pos) as *const c_char, 256, core::ptr::null_mut());
11155                            if consumed == !0usize || consumed == !1usize {
11156                                wbuf_local[wcount] = *pbytes.add(byte_pos) as c_int;
11157                                byte_pos += 1;
11158                                wcount += 1;
11159                            } else if consumed == 0 {
11160                                break;
11161                            } else {
11162                                wbuf_local[wcount] = wc_val;
11163                                byte_pos += consumed;
11164                                wcount += 1;
11165                            }
11166                        }
11167                        if flags & FLAG_MINUS != 0 {
11168                            for k in 0..wcount { wc!(wbuf_local[k]); }
11169                            for _ in 0..width.saturating_sub(wcount) { wc!(b' '); }
11170                        } else {
11171                            for _ in 0..width.saturating_sub(wcount) { wc!(b' '); }
11172                            for k in 0..wcount { wc!(wbuf_local[k]); }
11173                        }
11174                    }
11175                }
11176                b'p' => {
11177                    wc!(b'0'); wc!(b'x');
11178                    let p = args.next_arg::<*const c_void>();
11179                    let b = format_hex(p as u64, false);
11180                    for k in 0..b.1 { wc!(b.0[k]); }
11181                }
11182                b'n' => {
11183                    let p = args.next_arg::<*mut c_void>();
11184                    if !p.is_null() { *(p as *mut c_int) = pos as c_int; }
11185                }
11186                b'%' => { wc!(b'%'); }
11187                _ => { wc!(b'%'); wc!(spec); }
11188            }
11189        }
11190        fi += 1;
11191    }
11192    let null_pos = if pos < cap { pos } else { cap };
11193    *s.add(null_pos) = 0;
11194    if pos > cap { -1 } else { pos as c_int }
11195}
11196
11197#[no_mangle]
11198pub unsafe extern "C" fn swprintf(s: *mut wchar_t, n: usize, fmt: *const wchar_t, args: ...) -> c_int {
11199    vswprintf(s, n, fmt, args)
11200}
11201
11202#[no_mangle]
11203pub unsafe extern "C" fn vfwprintf(f: *mut FILE, fmt: *const wchar_t, args: VaList) -> c_int {
11204    let mut buf = [0i32; 4096];
11205    let r = vswprintf(buf.as_mut_ptr(), 4096, fmt, args);
11206    if r < 0 { return r; }
11207    let mut i = 0;
11208    while i < r as usize {
11209        let mut mb = [0u8; 4];
11210        let n = wcrtomb(mb.as_mut_ptr() as *mut c_char, buf[i], core::ptr::null_mut());
11211        if n == !0usize { return -1; }
11212        if fwrite(mb.as_ptr() as *const c_void, 1, n, f) != n { return -1; }
11213        i += 1;
11214    }
11215    r
11216}
11217
11218#[no_mangle]
11219pub unsafe extern "C" fn fwprintf(f: *mut FILE, fmt: *const wchar_t, args: ...) -> c_int {
11220    vfwprintf(f, fmt, args)
11221}
11222
11223// ============================================================
11224// wchar: vswscanf / swscanf / vfwscanf / fwscanf / vwscanf / wscanf
11225// ============================================================
11226
11227// Convert wchar_t format to byte format (ASCII cast, format specifiers are ASCII)
11228unsafe fn wcsfmt_to_mbs(dst: *mut u8, dst_len: usize, src: *const wchar_t) {
11229    let mut i = 0usize;
11230    loop {
11231        let ch = *src.add(i) as u32;
11232        if ch == 0 || i >= dst_len - 1 { break; }
11233        *dst.add(i) = if ch < 128 { ch as u8 } else { b'?' };
11234        i += 1;
11235    }
11236    *dst.add(i) = 0;
11237}
11238
11239#[no_mangle]
11240pub unsafe extern "C" fn vswscanf(s: *const wchar_t, fmt: *const wchar_t, mut args: VaList) -> c_int {
11241    let mut mbs_buf = [0u8; 4096];
11242    let mut i = 0usize;
11243    let mut mb_pos = 0usize;
11244    loop {
11245        let wc = *s.add(i) as c_int;
11246        if wc == 0 { break; }
11247        let mut mb = [0u8; 4];
11248        let n = wcrtomb(mb.as_mut_ptr() as *mut c_char, wc, core::ptr::null_mut());
11249        if n == !0usize || n == 0 { break; }
11250        if mb_pos + n >= mbs_buf.len() { break; }
11251        let mut k = 0usize;
11252        while k < n { mbs_buf[mb_pos + k] = mb[k]; k += 1; }
11253        mb_pos += n;
11254        i += 1;
11255    }
11256    mbs_buf[mb_pos] = 0;
11257    let mut mbs_fmt = [0u8; 4096];
11258    wcsfmt_to_mbs(mbs_fmt.as_mut_ptr(), mbs_fmt.len(), fmt);
11259    do_vsscanf(mbs_buf.as_ptr(), mb_pos, mbs_fmt.as_ptr() as *const c_char, &mut args, core::ptr::null_mut())
11260}
11261
11262#[no_mangle]
11263pub unsafe extern "C" fn swscanf(s: *const wchar_t, fmt: *const wchar_t, args: ...) -> c_int {
11264    vswscanf(s, fmt, args)
11265}
11266
11267#[no_mangle]
11268pub unsafe extern "C" fn vfwscanf(f: *mut FILE, fmt: *const wchar_t, mut args: VaList) -> c_int {
11269    let start_pos = ftello(f);
11270    if start_pos < 0 { return -1; }
11271    let mut buf = [0u8; 4096];
11272    let mut n = 0usize;
11273    loop {
11274        if n >= buf.len() - 1 { break; }
11275        let c = fgetc(f);
11276        if c == -1 { break; }
11277        buf[n] = c as u8;
11278        n += 1;
11279    }
11280    buf[n] = 0;
11281    let mut mbs_fmt = [0u8; 4096];
11282    wcsfmt_to_mbs(mbs_fmt.as_mut_ptr(), mbs_fmt.len(), fmt);
11283    let mut consumed = 0usize;
11284    let hit_eof = (*f)._eof != 0;
11285    let result = do_vsscanf(buf.as_ptr(), n, mbs_fmt.as_ptr() as *const c_char, &mut args, &mut consumed);
11286    let _ = fseeko(f, start_pos + consumed as i64, SEEK_SET);
11287    if hit_eof && consumed == n {
11288        (*f)._eof = 1;
11289    }
11290    result
11291}
11292
11293#[no_mangle]
11294pub unsafe extern "C" fn fwscanf(f: *mut FILE, fmt: *const wchar_t, args: ...) -> c_int {
11295    vfwscanf(f, fmt, args)
11296}
11297
11298#[no_mangle]
11299pub unsafe extern "C" fn vwscanf(fmt: *const wchar_t, args: VaList) -> c_int {
11300    vfwscanf(stdin, fmt, args)
11301}
11302
11303#[no_mangle]
11304pub unsafe extern "C" fn wscanf(fmt: *const wchar_t, args: ...) -> c_int {
11305    vfwscanf(stdin, fmt, args)
11306}
11307
11308// ============================================================
11309// Time: internal helpers
11310// ============================================================
11311
11312const LEAPOCH: i64 = 946684800 + 86400 * (31 + 29);
11313const DAYS_PER_400Y: i64 = 365 * 400 + 97;
11314const DAYS_PER_100Y: i64 = 365 * 100 + 24;
11315const DAYS_PER_4Y: i64 = 365 * 4 + 1;
11316
11317const SECS_THROUGH_MONTH: [i32; 12] = [
11318    0, 31*86400, 59*86400, 90*86400, 120*86400, 151*86400,
11319    181*86400, 212*86400, 243*86400, 273*86400, 304*86400, 334*86400,
11320];
11321
11322const DAYS_IN_MONTH: [i32; 12] = [31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29];
11323
11324unsafe fn year_to_secs(year: i64, is_leap: &mut bool) -> i64 {
11325    if year - 2 <= 136 {
11326        let y = year as i32;
11327        let mut leaps = (y - 68) >> 2;
11328        if (y - 68) & 3 == 0 { leaps -= 1; *is_leap = true; } else { *is_leap = false; }
11329        return 31536000 * (y as i64 - 70) + 86400 * leaps as i64;
11330    }
11331    let mut cycles = ((year - 100) / 400) as i32;
11332    let mut rem = ((year - 100) % 400) as i32;
11333    if rem < 0 { cycles -= 1; rem += 400; }
11334    let (centuries, leaps);
11335    if rem == 0 { *is_leap = true; centuries = 0; leaps = 0; }
11336    else {
11337        let c;
11338        if rem >= 200 { if rem >= 300 { c = 3; rem -= 300; } else { c = 2; rem -= 200; } }
11339        else { if rem >= 100 { c = 1; rem -= 100; } else { c = 0; } }
11340        centuries = c;
11341        if rem == 0 { *is_leap = false; leaps = 0; }
11342        else { leaps = rem / 4; rem %= 4; *is_leap = rem == 0; }
11343    }
11344    let total_leaps = leaps + 97 * cycles + 24 * centuries - (*is_leap as i32);
11345    (year - 100) * 31536000 + total_leaps as i64 * 86400 + 946684800 + 86400
11346}
11347
11348fn month_to_secs(month: i32, is_leap: bool) -> i32 {
11349    let mut t = SECS_THROUGH_MONTH[month as usize];
11350    if is_leap && month >= 2 { t += 86400; }
11351    t
11352}
11353
11354unsafe fn secs_to_tm(t: i64, tm: &mut tm) -> bool {
11355    if t < (i32::MIN as i64) * 31622400 || t > (i32::MAX as i64) * 31622400 { return false; }
11356    let secs = t - LEAPOCH;
11357    let mut days = secs / 86400;
11358    let mut remsecs = (secs % 86400) as i32;
11359    if remsecs < 0 { remsecs += 86400; days -= 1; }
11360    let mut wday = ((3 + days) % 7) as i32;
11361    if wday < 0 { wday += 7; }
11362    let mut qc_cycles = (days / DAYS_PER_400Y) as i32;
11363    let mut remdays = (days % DAYS_PER_400Y) as i32;
11364    if remdays < 0 { remdays += DAYS_PER_400Y as i32; qc_cycles -= 1; }
11365    let mut c_cycles = remdays / DAYS_PER_100Y as i32;
11366    if c_cycles == 4 { c_cycles -= 1; }
11367    remdays -= c_cycles * DAYS_PER_100Y as i32;
11368    let mut q_cycles = remdays / DAYS_PER_4Y as i32;
11369    if q_cycles == 25 { q_cycles -= 1; }
11370    remdays -= q_cycles * DAYS_PER_4Y as i32;
11371    let mut remyears = remdays / 365;
11372    if remyears == 4 { remyears -= 1; }
11373    remdays -= remyears * 365;
11374    let leap = remyears == 0 && (q_cycles != 0 || c_cycles == 0);
11375    let mut yday = remdays + 31 + 28 + leap as i32;
11376    if yday >= 365 + leap as i32 { yday -= 365 + leap as i32; }
11377    let years = remyears as i64 + 4 * q_cycles as i64 + 100 * c_cycles as i64 + 400 * qc_cycles as i64;
11378    let mut months = 0i32;
11379    let mut rd = remdays;
11380    for m in 0..12 {
11381        if DAYS_IN_MONTH[m] <= rd { rd -= DAYS_IN_MONTH[m]; } else { months = m as i32; break; }
11382    }
11383    let mut adj_years = years;
11384    if months >= 10 { months -= 12; adj_years += 1; }
11385    if adj_years + 100 > i32::MAX as i64 || adj_years + 100 < i32::MIN as i64 { return false; }
11386    tm.tm_year = (adj_years + 100) as c_int;
11387    tm.tm_mon = months + 2;
11388    tm.tm_mday = rd + 1;
11389    tm.tm_wday = wday;
11390    tm.tm_yday = yday;
11391    tm.tm_hour = remsecs / 3600;
11392    tm.tm_min = remsecs / 60 % 60;
11393    tm.tm_sec = remsecs % 60;
11394    true
11395}
11396
11397unsafe fn tm_to_secs(tm: *const tm) -> i64 {
11398    let mut year = (*tm).tm_year as i64;
11399    let mut month = (*tm).tm_mon;
11400    if month >= 12 || month < 0 {
11401        let adj = month / 12;
11402        month %= 12;
11403        if month < 0 { month += 12; }
11404        year += adj as i64;
11405    }
11406    let mut is_leap = false;
11407    let mut t = year_to_secs(year, &mut is_leap);
11408    t += month_to_secs(month, is_leap) as i64;
11409    t += 86400 * ((*tm).tm_mday - 1) as i64;
11410    t += 3600 * (*tm).tm_hour as i64;
11411    t += 60 * (*tm).tm_min as i64;
11412    t += (*tm).tm_sec as i64;
11413    t
11414}
11415
11416static mut TM_BUF: tm = tm {
11417    tm_sec: 0, tm_min: 0, tm_hour: 0, tm_mday: 0, tm_mon: 0,
11418    tm_year: 0, tm_wday: 0, tm_yday: 0, tm_isdst: 0,
11419    tm_gmtoff: 0, tm_zone: core::ptr::null(),
11420};
11421
11422static mut UTC_STR: [c_char; 4] = [b'U' as c_char, b'T' as c_char, b'C' as c_char, 0];
11423
11424const CLOCK_PROCESS_CPUTIME_ID: c_int = 2;
11425
11426// ============================================================
11427// Time: core functions
11428// ============================================================
11429
11430#[no_mangle]
11431pub unsafe extern "C" fn time(t: *mut TimeT) -> TimeT {
11432    let mut ts: timespec = core::mem::zeroed();
11433    let _ = sys_clock_gettime(CLOCK_REALTIME, &mut ts);
11434    if !t.is_null() { *t = ts.tv_sec; }
11435    ts.tv_sec
11436}
11437
11438#[no_mangle]
11439pub unsafe extern "C" fn difftime(t1: TimeT, t0: TimeT) -> f64 {
11440    (t1 as i64 - t0 as i64) as f64
11441}
11442
11443#[no_mangle]
11444pub unsafe extern "C" fn clock() -> ClockT {
11445    let mut ts: timespec = core::mem::zeroed();
11446    let _ = sys_clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &mut ts);
11447    ts.tv_sec * 1000000 + ts.tv_nsec / 1000
11448}
11449
11450#[no_mangle]
11451pub unsafe extern "C" fn gmtime(t: *const TimeT) -> *mut tm {
11452    gmtime_r(t, &raw mut TM_BUF)
11453}
11454
11455#[no_mangle]
11456pub unsafe extern "C" fn gmtime_r(t: *const TimeT, tm: *mut tm) -> *mut tm {
11457    if !secs_to_tm(*t as i64, &mut *tm) { ERRNO!() = EOVERFLOW; return core::ptr::null_mut(); }
11458    (*tm).tm_isdst = 0;
11459    (*tm).tm_gmtoff = 0;
11460    (*tm).tm_zone = core::ptr::addr_of!(UTC_STR).cast::<c_char>();
11461    tm
11462}
11463
11464#[no_mangle]
11465pub unsafe extern "C" fn localtime(t: *const TimeT) -> *mut tm { gmtime(t) }
11466
11467#[no_mangle]
11468pub unsafe extern "C" fn localtime_r(t: *const TimeT, tm: *mut tm) -> *mut tm { gmtime_r(t, tm) }
11469
11470#[no_mangle]
11471pub unsafe extern "C" fn mktime(tm: *mut tm) -> TimeT {
11472    let t = tm_to_secs(tm as *const tm);
11473    if !secs_to_tm(t, &mut *tm) { ERRNO!() = EOVERFLOW; return -1; }
11474    (*tm).tm_isdst = 0;
11475    (*tm).tm_gmtoff = 0;
11476    (*tm).tm_zone = core::ptr::addr_of!(UTC_STR).cast::<c_char>();
11477    t as TimeT
11478}
11479
11480#[no_mangle]
11481pub unsafe extern "C" fn timegm(tm: *mut tm) -> TimeT {
11482    let t = tm_to_secs(tm as *const tm);
11483    if !secs_to_tm(t, &mut *tm) { ERRNO!() = EOVERFLOW; return -1; }
11484    (*tm).tm_isdst = 0;
11485    (*tm).tm_gmtoff = 0;
11486    (*tm).tm_zone = core::ptr::addr_of!(UTC_STR).cast::<c_char>();
11487    t as TimeT
11488}
11489
11490static mut ASCTIME_BUF: [c_char; 26] = [0; 26];
11491
11492const DAY_NAMES: [&[u8]; 7] = [b"Sun\0", b"Mon\0", b"Tue\0", b"Wed\0", b"Thu\0", b"Fri\0", b"Sat\0"];
11493const MON_NAMES: [&[u8]; 12] = [b"Jan\0", b"Feb\0", b"Mar\0", b"Apr\0", b"May\0", b"Jun\0", b"Jul\0", b"Aug\0", b"Sep\0", b"Oct\0", b"Nov\0", b"Dec\0"];
11494
11495#[no_mangle]
11496pub unsafe extern "C" fn asctime(tm: *const tm) -> *mut c_char {
11497    asctime_r(tm, core::ptr::addr_of_mut!(ASCTIME_BUF).cast::<c_char>())
11498}
11499
11500#[no_mangle]
11501pub unsafe extern "C" fn asctime_r(tm: *const tm, buf: *mut c_char) -> *mut c_char {
11502    let wday = if (*tm).tm_wday >= 0 && (*tm).tm_wday < 7 { (*tm).tm_wday as usize } else { 0 };
11503    let mon = if (*tm).tm_mon >= 0 && (*tm).tm_mon < 12 { (*tm).tm_mon as usize } else { 0 };
11504    let dn = DAY_NAMES[wday];
11505    let mn = MON_NAMES[mon];
11506    let mut p = 0;
11507    for k in 0..3 { *buf.add(p) = *dn.as_ptr().add(k) as c_char; p += 1; }
11508    *buf.add(p) = b' ' as c_char; p += 1;
11509    for k in 0..3 { *buf.add(p) = *mn.as_ptr().add(k) as c_char; p += 1; }
11510    *buf.add(p) = b' ' as c_char; p += 1;
11511    let v = (*tm).tm_mday;
11512    *buf.add(p) = (b'0' + (v / 10) as u8) as c_char; p += 1;
11513    *buf.add(p) = (b'0' + (v % 10) as u8) as c_char; p += 1;
11514    *buf.add(p) = b' ' as c_char; p += 1;
11515    let hh = (*tm).tm_hour;
11516    *buf.add(p) = (b'0' + (hh / 10) as u8) as c_char; p += 1;
11517    *buf.add(p) = (b'0' + (hh % 10) as u8) as c_char; p += 1;
11518    *buf.add(p) = b':' as c_char; p += 1;
11519    let mm = (*tm).tm_min;
11520    *buf.add(p) = (b'0' + (mm / 10) as u8) as c_char; p += 1;
11521    *buf.add(p) = (b'0' + (mm % 10) as u8) as c_char; p += 1;
11522    *buf.add(p) = b':' as c_char; p += 1;
11523    let ss = (*tm).tm_sec;
11524    *buf.add(p) = (b'0' + (ss / 10) as u8) as c_char; p += 1;
11525    *buf.add(p) = (b'0' + (ss % 10) as u8) as c_char; p += 1;
11526    *buf.add(p) = b' ' as c_char; p += 1;
11527    let yr = (*tm).tm_year + 1900;
11528    let mut tmp = [0u8; 4];
11529    let mut v2 = yr;
11530    for k in (0..4).rev() { tmp[k] = b'0' + (v2 % 10) as u8; v2 /= 10; }
11531    for k in 0..4 { *buf.add(p) = tmp[k] as c_char; p += 1; }
11532    *buf.add(p) = b'\n' as c_char; p += 1;
11533    *buf.add(p) = 0;
11534    buf
11535}
11536
11537#[no_mangle]
11538pub unsafe extern "C" fn ctime(t: *const TimeT) -> *mut c_char { asctime(gmtime(t)) }
11539
11540#[no_mangle]
11541pub unsafe extern "C" fn ctime_r(t: *const TimeT, buf: *mut c_char) -> *mut c_char {
11542    let mut tmp: tm = core::mem::zeroed();
11543    gmtime_r(t, &mut tmp);
11544    asctime_r(&tmp, buf)
11545}
11546
11547// ============================================================
11548// Time: clock_getres / clock_settime / clock_nanosleep / gettimeofday
11549// ============================================================
11550
11551unsafe fn sys_clock_getres(clockid: c_int, ts: *mut timespec) -> i64 {
11552    <Arch as Syscalls>::syscall2(SYS_CLOCK_GETRES, clockid as i64, ts as i64)
11553}
11554
11555unsafe fn sys_clock_settime(clockid: c_int, ts: *const timespec) -> i64 {
11556    <Arch as Syscalls>::syscall2(SYS_CLOCK_SETTIME, clockid as i64, ts as i64)
11557}
11558
11559unsafe fn sys_clock_nanosleep(clockid: c_int, flags: c_int, req: *const timespec, rem: *mut timespec) -> i64 {
11560    <Arch as Syscalls>::syscall4(SYS_CLOCK_NANOSLEEP, clockid as i64, flags as i64, req as i64, rem as i64)
11561}
11562
11563#[no_mangle]
11564pub unsafe extern "C" fn clock_getres(clockid: c_int, ts: *mut timespec) -> c_int {
11565    let r = sys_clock_getres(clockid, ts);
11566    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
11567}
11568
11569#[no_mangle]
11570pub unsafe extern "C" fn clock_settime(clockid: c_int, ts: *const timespec) -> c_int {
11571    let r = sys_clock_settime(clockid, ts);
11572    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
11573}
11574
11575#[no_mangle]
11576pub unsafe extern "C" fn clock_nanosleep(clockid: c_int, flags: c_int, req: *const timespec, rem: *mut timespec) -> c_int {
11577    let r = sys_clock_nanosleep(clockid, flags, req, rem);
11578    if r < 0 {
11579        let e = (-r) as c_int;
11580        // The kernel returns EOPNOTSUPP for CLOCK_THREAD_CPUTIME_ID;
11581        // POSIX/musl report EINVAL instead.
11582        if clockid == 3 && e == 95 { return 22; }
11583        e
11584    } else {
11585        0
11586    }
11587}
11588
11589#[no_mangle]
11590pub unsafe extern "C" fn gettimeofday(tv: *mut timeval, _tz: *mut c_void) -> c_int {
11591    if tv.is_null() { return 0; }
11592    let mut ts: timespec = core::mem::zeroed();
11593    let _ = sys_clock_gettime(CLOCK_REALTIME, &mut ts);
11594    (*tv).tv_sec = ts.tv_sec;
11595    (*tv).tv_usec = ts.tv_nsec / 1000;
11596    0
11597}
11598
11599// ============================================================
11600// Time: strftime
11601// ============================================================
11602
11603fn is_leap_year(y: i32) -> bool {
11604    let yr = if y > i32::MAX - 1900 { y - 2000 } else { y + 1900 };
11605    yr % 4 == 0 && (yr % 100 != 0 || yr % 400 == 0)
11606}
11607
11608fn week_num(tm: &tm) -> i32 {
11609    let mut val = (tm.tm_yday + 7 - ((tm.tm_wday + 6) % 7)) / 7;
11610    if (tm.tm_wday + 371 - tm.tm_yday - 2).rem_euclid(7) <= 2 { val += 1; }
11611    if val == 0 {
11612        val = 52;
11613        let dec31 = (tm.tm_wday + 7 - tm.tm_yday - 1).rem_euclid(7);
11614        if dec31 == 4 || (dec31 == 5 && is_leap_year(tm.tm_year % 400 - 1)) { val += 1; }
11615    } else if val == 53 {
11616        let jan1 = (tm.tm_wday + 371 - tm.tm_yday).rem_euclid(7);
11617        if jan1 != 4 && (jan1 != 3 || !is_leap_year(tm.tm_year)) { val = 1; }
11618    }
11619    val
11620}
11621
11622// Langinfo constants for strftime
11623const ABDAY_1: c_int = 0x20000;
11624const DAY_1: c_int = 0x20007;
11625const ABMON_1: c_int = 0x2000E;
11626const MON_1: c_int = 0x2001A;
11627const AM_STR_LI: c_int = 0x20026;
11628const PM_STR_LI: c_int = 0x20027;
11629const D_T_FMT: c_int = 0x20028;
11630const D_FMT: c_int = 0x20029;
11631const T_FMT: c_int = 0x2002A;
11632const T_FMT_AMPM: c_int = 0x2002B;
11633
11634// Format an i64 into buf as decimal with zero/sign padding. Returns (ptr_to_start, length).
11635// pad: '-' = no pad, '_' = space pad, '0' or 0 = zero pad. width: minimum digits (excluding sign).
11636unsafe fn num_fmt(buf: &mut [u8; 100], val: i64, pad: u8, width: usize) -> (*const u8, usize) {
11637    let negative = val < 0;
11638    let mut v = if negative { -val as u64 } else { val as u64 };
11639    let mut tmp = [0u8; 24];
11640    let mut pos = 24usize;
11641    if v == 0 { pos -= 1; tmp[pos] = b'0'; }
11642    while v > 0 { pos -= 1; tmp[pos] = b'0' + (v % 10) as u8; v /= 10; }
11643    let digits = 24 - pos;
11644    let sign_chars = if negative { 1 } else { 0 };
11645    let needed = sign_chars + digits;
11646    let fill = if width > needed { width - needed } else { 0 };
11647    let total = fill + needed;
11648    let mut p = 0usize;
11649    match pad {
11650        b'-' => { /* no fill */ }
11651        b'_' => {
11652            for _ in 0..fill { buf[p] = b' '; p += 1; }
11653            if negative { buf[p] = b'-'; p += 1; }
11654        }
11655        _ => {
11656            if negative { buf[p] = b'-'; p += 1; }
11657            for _ in 0..fill { buf[p] = b'0'; p += 1; }
11658        }
11659    }
11660    for i in 0..digits { buf[p] = tmp[pos + i]; p += 1; }
11661    (buf.as_ptr(), total)
11662}
11663
11664// Copy a null-terminated C string into buf. Returns (ptr, len).
11665unsafe fn str_to_buf(buf: &mut [u8; 100], s: *const c_char) -> (*const u8, usize) {
11666    let mut k = 0usize;
11667    while *s.add(k) != 0 {
11668        buf[k] = *s.add(k) as u8;
11669        k += 1;
11670    }
11671    (buf.as_ptr(), k)
11672}
11673
11674// Core format dispatcher. Returns (ptr, len) where ptr points into buf or a static string.
11675// pad=0 means use default; returns (null, 0) for unknown specifiers.
11676unsafe fn __strftime_fmt_1(buf: &mut [u8; 100], f: u8, tm: *const tm, pad: u8) -> (*const u8, usize) {
11677    let mut val: i64;
11678    let mut width: usize = 2;
11679    let mut def_pad: u8 = b'0';
11680    let fmt: *const c_char;
11681
11682    match f {
11683        b'a' => {
11684            if (*tm).tm_wday as u32 > 6 { return (b"\0".as_ptr(), 0); }
11685            let p = nl_langinfo(ABDAY_1 + (*tm).tm_wday);
11686            return str_to_buf(buf, p);
11687        }
11688        b'A' => {
11689            if (*tm).tm_wday as u32 > 6 { return (b"\0".as_ptr(), 0); }
11690            let p = nl_langinfo(DAY_1 + (*tm).tm_wday);
11691            return str_to_buf(buf, p);
11692        }
11693        b'h' | b'b' => {
11694            if (*tm).tm_mon as u32 > 11 { return (b"\0".as_ptr(), 0); }
11695            let p = nl_langinfo(ABMON_1 + (*tm).tm_mon);
11696            return str_to_buf(buf, p);
11697        }
11698        b'B' => {
11699            if (*tm).tm_mon as u32 > 11 { return (b"\0".as_ptr(), 0); }
11700            let p = nl_langinfo(MON_1 + (*tm).tm_mon);
11701            return str_to_buf(buf, p);
11702        }
11703        b'c' => {
11704            let p = nl_langinfo(D_T_FMT);
11705            let l = __strftime_l(buf.as_mut_ptr() as *mut c_char, 100, p, tm);
11706            return (buf.as_ptr(), l);
11707        }
11708        b'C' => {
11709            val = (1900i64 + (*tm).tm_year as i64) / 100;
11710            return num_fmt(buf, val, if pad != 0 { pad } else { def_pad }, width);
11711        }
11712        b'e' => { def_pad = b'_'; }
11713        b'd' => {
11714            val = (*tm).tm_mday as i64;
11715            return num_fmt(buf, val, if pad != 0 { pad } else { def_pad }, width);
11716        }
11717        b'D' => {
11718            fmt = b"%m/%d/%y\0".as_ptr() as *const c_char;
11719            let l = __strftime_l(buf.as_mut_ptr() as *mut c_char, 100, fmt, tm);
11720            return (buf.as_ptr(), l);
11721        }
11722        b'F' => {
11723            fmt = b"%Y-%m-%d\0".as_ptr() as *const c_char;
11724            let l = __strftime_l(buf.as_mut_ptr() as *mut c_char, 100, fmt, tm);
11725            return (buf.as_ptr(), l);
11726        }
11727        b'g' | b'G' => {
11728            val = (*tm).tm_year as i64 + 1900;
11729            if (*tm).tm_yday < 3 && week_num(&*tm) != 1 { val -= 1; }
11730            else if (*tm).tm_yday > 360 && week_num(&*tm) == 1 { val += 1; }
11731            if f == b'g' { val %= 100; }
11732            else { width = 4; }
11733            return num_fmt(buf, val, if pad != 0 { pad } else { def_pad }, width);
11734        }
11735        b'H' => {
11736            val = (*tm).tm_hour as i64;
11737            return num_fmt(buf, val, if pad != 0 { pad } else { def_pad }, width);
11738        }
11739        b'I' => {
11740            val = (*tm).tm_hour as i64;
11741            if val == 0 { val = 12; }
11742            else if val > 12 { val -= 12; }
11743            return num_fmt(buf, val, if pad != 0 { pad } else { def_pad }, width);
11744        }
11745        b'j' => {
11746            val = (*tm).tm_yday as i64 + 1;
11747            width = 3;
11748            return num_fmt(buf, val, if pad != 0 { pad } else { def_pad }, width);
11749        }
11750        b'm' => {
11751            val = (*tm).tm_mon as i64 + 1;
11752            return num_fmt(buf, val, if pad != 0 { pad } else { def_pad }, width);
11753        }
11754        b'M' => {
11755            val = (*tm).tm_min as i64;
11756            return num_fmt(buf, val, if pad != 0 { pad } else { def_pad }, width);
11757        }
11758        b'n' => { buf[0] = b'\n'; return (buf.as_ptr(), 1); }
11759        b'p' => {
11760            let p = if (*tm).tm_hour >= 12 { nl_langinfo(PM_STR_LI) } else { nl_langinfo(AM_STR_LI) };
11761            return str_to_buf(buf, p);
11762        }
11763        b'r' => {
11764            let p = nl_langinfo(T_FMT_AMPM);
11765            let l = __strftime_l(buf.as_mut_ptr() as *mut c_char, 100, p, tm);
11766            return (buf.as_ptr(), l);
11767        }
11768        b'R' => {
11769            fmt = b"%H:%M\0".as_ptr() as *const c_char;
11770            let l = __strftime_l(buf.as_mut_ptr() as *mut c_char, 100, fmt, tm);
11771            return (buf.as_ptr(), l);
11772        }
11773        b's' => {
11774            val = tm_to_secs(tm) - (*tm).tm_gmtoff as i64;
11775            width = 1;
11776            return num_fmt(buf, val, if pad != 0 { pad } else { def_pad }, width);
11777        }
11778        b'S' => {
11779            val = (*tm).tm_sec as i64;
11780            return num_fmt(buf, val, if pad != 0 { pad } else { def_pad }, width);
11781        }
11782        b't' => { buf[0] = b'\t'; return (buf.as_ptr(), 1); }
11783        b'T' => {
11784            fmt = b"%H:%M:%S\0".as_ptr() as *const c_char;
11785            let l = __strftime_l(buf.as_mut_ptr() as *mut c_char, 100, fmt, tm);
11786            return (buf.as_ptr(), l);
11787        }
11788        b'u' => {
11789            val = if (*tm).tm_wday != 0 { (*tm).tm_wday as i64 } else { 7 };
11790            width = 1;
11791            return num_fmt(buf, val, if pad != 0 { pad } else { def_pad }, width);
11792        }
11793        b'U' => {
11794            val = ((*tm).tm_yday as u64 + 7 - (*tm).tm_wday as u64) as i64 / 7;
11795            return num_fmt(buf, val, if pad != 0 { pad } else { def_pad }, width);
11796        }
11797        b'W' => {
11798            val = ((*tm).tm_yday as u64 + 7 - ((*tm).tm_wday as u64 + 6) % 7) as i64 / 7;
11799            return num_fmt(buf, val, if pad != 0 { pad } else { def_pad }, width);
11800        }
11801        b'V' => {
11802            val = week_num(&*tm) as i64;
11803            return num_fmt(buf, val, if pad != 0 { pad } else { def_pad }, width);
11804        }
11805        b'w' => {
11806            val = (*tm).tm_wday as i64;
11807            width = 1;
11808            return num_fmt(buf, val, if pad != 0 { pad } else { def_pad }, width);
11809        }
11810        b'x' => {
11811            let p = nl_langinfo(D_FMT);
11812            let l = __strftime_l(buf.as_mut_ptr() as *mut c_char, 100, p, tm);
11813            return (buf.as_ptr(), l);
11814        }
11815        b'X' => {
11816            let p = nl_langinfo(T_FMT);
11817            let l = __strftime_l(buf.as_mut_ptr() as *mut c_char, 100, p, tm);
11818            return (buf.as_ptr(), l);
11819        }
11820        b'y' => {
11821            val = (1900i64 + (*tm).tm_year as i64) % 100;
11822            if val < 0 { val = -val; }
11823            return num_fmt(buf, val, if pad != 0 { pad } else { def_pad }, width);
11824        }
11825        b'Y' => {
11826            val = (*tm).tm_year as i64 + 1900;
11827            if val >= 10000 {
11828                let mut tmp = [0u8; 100];
11829                let (_, k) = num_fmt(&mut tmp, val, b'-', 1);
11830                buf[0] = b'+';
11831                for i in 0..k { buf[1 + i] = tmp[i]; }
11832                return (buf.as_ptr(), 1 + k);
11833            }
11834            width = 4;
11835            return num_fmt(buf, val, if pad != 0 { pad } else { def_pad }, width);
11836        }
11837        b'z' => {
11838            if (*tm).tm_isdst < 0 { return (b"\0".as_ptr(), 0); }
11839            let gmtoff = (*tm).tm_gmtoff as i64;
11840            let sign = if gmtoff >= 0 { b'+' } else { b'-' };
11841            let off = if gmtoff < 0 { -gmtoff } else { gmtoff };
11842            let hh = (off / 3600) as i32;
11843            let mm = ((off % 3600) / 60) as i32;
11844            buf[0] = sign;
11845            buf[1] = b'0' + (hh / 10) as u8;
11846            buf[2] = b'0' + (hh % 10) as u8;
11847            buf[3] = b'0' + (mm / 10) as u8;
11848            buf[4] = b'0' + (mm % 10) as u8;
11849            return (buf.as_ptr(), 5);
11850        }
11851        b'Z' => {
11852            if (*tm).tm_isdst < 0 || (*tm).tm_zone.is_null() { return (b"\0".as_ptr(), 0); }
11853            return str_to_buf(buf, (*tm).tm_zone);
11854        }
11855        b'%' => { buf[0] = b'%'; return (buf.as_ptr(), 1); }
11856        _ => { return (core::ptr::null(), 0); }
11857    }
11858    // fallthrough for 'e' (def_pad='_', same logic as 'd')
11859    val = (*tm).tm_mday as i64;
11860    num_fmt(buf, val, if pad != 0 { pad } else { def_pad }, width)
11861}
11862
11863unsafe fn __strftime_l(s: *mut c_char, n: usize, f: *const c_char, tm: *const tm) -> usize {
11864    let mut l: usize = 0;
11865    let mut fi: usize = 0;
11866    while l < n {
11867        let ch = *f.add(fi) as u8;
11868        if ch == 0 {
11869            *s.add(l) = 0;
11870            return l;
11871        }
11872        if ch != b'%' {
11873            *s.add(l) = ch as c_char;
11874            l += 1;
11875            fi += 1;
11876            continue;
11877        }
11878        fi += 1;
11879        // Parse flags
11880        let mut pad: u8 = 0;
11881        let fc = *f.add(fi) as u8;
11882        if fc == b'-' || fc == b'_' || fc == b'0' { pad = fc; fi += 1; }
11883        let plus = *f.add(fi) as u8 == b'+';
11884        if plus { fi += 1; }
11885        // Parse width
11886        let mut width: usize = 0;
11887        let mut p_idx = fi;
11888        {
11889            let fc2 = *f.add(fi) as u8;
11890            if fc2 >= b'0' && fc2 <= b'9' {
11891                let mut w: usize = 0;
11892                while { let c = *f.add(p_idx) as u8; c >= b'0' && c <= b'9' } {
11893                    w = w * 10 + (*f.add(p_idx) as usize - b'0' as usize);
11894                    p_idx += 1;
11895                }
11896                width = w;
11897            }
11898        }
11899        let spec_ch = *f.add(p_idx) as u8;
11900        if spec_ch == b'C' || spec_ch == b'F' || spec_ch == b'G' || spec_ch == b'Y' {
11901            if width == 0 && p_idx != fi { width = 1; }
11902        } else {
11903            width = 0;
11904        }
11905        fi = p_idx;
11906        // Skip E/O modifier
11907        let fc3 = *f.add(fi) as u8;
11908        if fc3 == b'E' || fc3 == b'O' { fi += 1; }
11909        let fch = *f.add(fi) as u8;
11910        // Call formatter
11911        let mut buf = [0u8; 100];
11912        let (t, k) = __strftime_fmt_1(&mut buf, fch, tm, pad);
11913        if t.is_null() { break; }
11914        let mut k = k;
11915        let mut t = t;
11916        if width != 0 {
11917            // Strip sign and leading zeros, count remaining digits
11918            if *t == b'+' || *t == b'-' { t = t.add(1); k -= 1; }
11919            while k > 0 && *t == b'0' && k > 1 && *t.add(1) >= b'0' && *t.add(1) <= b'9' {
11920                t = t.add(1); k -= 1;
11921            }
11922            if width < k { width = k; }
11923            let mut d: usize = 0;
11924            while d < k && *t.add(d) >= b'0' && *t.add(d) <= b'9' { d += 1; }
11925            if (*tm).tm_year < -1900 {
11926                if l < n { *s.add(l) = b'-' as c_char; }
11927                l += 1;
11928                width -= 1;
11929            } else if plus && d + (width - k) >= (if spec_ch == b'C' { 3 } else { 5 }) {
11930                if l < n { *s.add(l) = b'+' as c_char; }
11931                l += 1;
11932                width -= 1;
11933            }
11934            while width > k && l < n {
11935                *s.add(l) = b'0' as c_char;
11936                l += 1;
11937                width -= 1;
11938            }
11939        }
11940        let copy = if k > n - l { n - l } else { k };
11941        core::ptr::copy_nonoverlapping(t, s.add(l) as *mut u8, copy);
11942        l += copy;
11943        fi += 1;
11944    }
11945    if n > 0 {
11946        if l == n { l = n - 1; }
11947        *s.add(l) = 0;
11948    }
11949    0
11950}
11951
11952#[no_mangle]
11953pub unsafe extern "C" fn strftime(s: *mut c_char, maxsize: usize, fmt: *const c_char, tm: *const tm) -> usize {
11954    __strftime_l(s, maxsize, fmt, tm)
11955}
11956
11957// ============================================================
11958// Time: strptime
11959// ============================================================
11960
11961unsafe fn match_prefix(s: *const u8, prefix: &[u8]) -> bool {
11962    for i in 0..prefix.len() {
11963        if *s.add(i) == 0 || *s.add(i) != prefix[i] { return false; }
11964    }
11965    true
11966}
11967
11968unsafe fn match_ci_prefix(s: *const u8, prefix: &[u8]) -> bool {
11969    for i in 0..prefix.len() {
11970        let c = *s.add(i);
11971        if c == 0 { return false; }
11972        if c.to_ascii_lowercase() != prefix[i].to_ascii_lowercase() { return false; }
11973    }
11974    true
11975}
11976
11977unsafe fn parse_int(p: *const u8, max_digits: usize) -> (i32, *const u8) {
11978    let mut val = 0i32;
11979    let mut np = p;
11980    let mut count = 0;
11981    while *np >= b'0' && *np <= b'9' && count < max_digits {
11982        val = val * 10 + (*np - b'0') as i32;
11983        np = np.add(1);
11984        count += 1;
11985    }
11986    if count == 0 { (0, p) } else { (val, np) }
11987}
11988
11989unsafe fn parse_range(p: *const u8, min: i32, max: i32) -> (i32, *const u8) {
11990    let (val, np) = parse_int(p, 4);
11991    if np == p || val < min || val > max { (0, p) } else { (val, np) }
11992}
11993
11994fn is_ascii_space(c: u8) -> bool {
11995    c == b' ' || c == b'\t' || c == b'\n' || c == b'\r' || c == 0x0c || c == 0x0b
11996}
11997
11998#[no_mangle]
11999pub unsafe extern "C" fn strptime(s: *const c_char, fmt: *const c_char, tm: *mut tm) -> *mut c_char {
12000    let mut p = s as *const u8;
12001    let mut f = fmt as *const u8;
12002    let mut want_century = 0i32;
12003    let mut century = 0i32;
12004    let mut relyear = 0i32;
12005    while *f != 0 {
12006        if *f != b'%' {
12007            if is_ascii_space(*f) {
12008                while *p != 0 && is_ascii_space(*p) { p = p.add(1); }
12009            } else {
12010                if *p != *f { return core::ptr::null_mut(); }
12011                p = p.add(1);
12012            }
12013            f = f.add(1);
12014            continue;
12015        }
12016        f = f.add(1);
12017        if *f == b'+' { f = f.add(1); }
12018        while *f >= b'0' && *f <= b'9' { f = f.add(1); }
12019        if *f == b'E' || *f == b'O' { f = f.add(1); }
12020        match *f {
12021            b'a' | b'A' => {
12022                let mut found = false;
12023                for i in 0..7 {
12024                    let names: [&[u8]; 7] = [b"Sunday", b"Monday", b"Tuesday", b"Wednesday", b"Thursday", b"Friday", b"Saturday"];
12025                    let snames: [&[u8]; 7] = [b"Sun", b"Mon", b"Tue", b"Wed", b"Thu", b"Fri", b"Sat"];
12026                    if match_prefix(p, names[i]) { (*tm).tm_wday = i as c_int; p = p.add(names[i].len()); found = true; break; }
12027                    if match_prefix(p, snames[i]) { (*tm).tm_wday = i as c_int; p = p.add(snames[i].len()); found = true; break; }
12028                }
12029                if !found { return core::ptr::null_mut(); }
12030            }
12031            b'b' | b'B' | b'h' => {
12032                let mut found = false;
12033                for i in 0..12 {
12034                    let names: [&[u8]; 12] = [b"January", b"February", b"March", b"April", b"May", b"June", b"July", b"August", b"September", b"October", b"November", b"December"];
12035                    let snames: [&[u8]; 12] = [b"Jan", b"Feb", b"Mar", b"Apr", b"May", b"Jun", b"Jul", b"Aug", b"Sep", b"Oct", b"Nov", b"Dec"];
12036                    if match_prefix(p, names[i]) { (*tm).tm_mon = i as c_int; p = p.add(names[i].len()); found = true; break; }
12037                    if match_prefix(p, snames[i]) { (*tm).tm_mon = i as c_int; p = p.add(snames[i].len()); found = true; break; }
12038                }
12039                if !found { return core::ptr::null_mut(); }
12040            }
12041            b'C' => {
12042                let (v, np) = parse_int(p, 2);
12043                if np == p { return core::ptr::null_mut(); }
12044                century = v;
12045                want_century |= 2;
12046                p = np;
12047            }
12048            b'd' | b'e' => {
12049                let (v, np) = parse_range(p, 1, 31);
12050                if np == p { return core::ptr::null_mut(); }
12051                (*tm).tm_mday = v;
12052                p = np;
12053            }
12054            b'D' => {
12055                let r = strptime(p as *const c_char, b"%m/%d/%y\0".as_ptr() as *const c_char, tm);
12056                if r.is_null() { return core::ptr::null_mut(); }
12057                p = r as *const u8;
12058            }
12059            b'F' => {
12060                let (yv, ynp) = parse_int(p, 4);
12061                if ynp == p { return core::ptr::null_mut(); }
12062                (*tm).tm_year = yv - 1900;
12063                p = ynp;
12064                if *p != b'-' { return core::ptr::null_mut(); }
12065                p = p.add(1);
12066                let (mv, mnp) = parse_range(p, 1, 12);
12067                if mnp == p { return core::ptr::null_mut(); }
12068                (*tm).tm_mon = mv - 1;
12069                p = mnp;
12070                if *p != b'-' { return core::ptr::null_mut(); }
12071                p = p.add(1);
12072                let (dv, dnp) = parse_range(p, 1, 31);
12073                if dnp == p { return core::ptr::null_mut(); }
12074                (*tm).tm_mday = dv;
12075                p = dnp;
12076            }
12077            b'H' | b'I' => {
12078                let (v, np) = parse_range(p, 0, 23);
12079                if np == p { return core::ptr::null_mut(); }
12080                (*tm).tm_hour = v;
12081                p = np;
12082            }
12083            b'j' => {
12084                let (v, np) = parse_range(p, 1, 366);
12085                if np == p { return core::ptr::null_mut(); }
12086                (*tm).tm_yday = v - 1;
12087                p = np;
12088            }
12089            b'm' => {
12090                let (v, np) = parse_range(p, 1, 12);
12091                if np == p { return core::ptr::null_mut(); }
12092                (*tm).tm_mon = v - 1;
12093                p = np;
12094            }
12095            b'M' => {
12096                let (v, np) = parse_range(p, 0, 59);
12097                if np == p { return core::ptr::null_mut(); }
12098                (*tm).tm_min = v;
12099                p = np;
12100            }
12101            b'n' | b't' => {
12102                while *p != 0 && is_ascii_space(*p) { p = p.add(1); }
12103            }
12104            b'p' => {
12105                if match_ci_prefix(p, b"AM") {
12106                    if (*tm).tm_hour == 12 { (*tm).tm_hour = 0; }
12107                    p = p.add(2);
12108                } else if match_ci_prefix(p, b"PM") {
12109                    if (*tm).tm_hour < 12 { (*tm).tm_hour += 12; }
12110                    p = p.add(2);
12111                } else { return core::ptr::null_mut(); }
12112            }
12113            b'r' => {
12114                let r = strptime(p as *const c_char, b"%I:%M:%S %p\0".as_ptr() as *const c_char, tm);
12115                if r.is_null() { return core::ptr::null_mut(); }
12116                p = r as *const u8;
12117            }
12118            b'R' => {
12119                let r = strptime(p as *const c_char, b"%H:%M\0".as_ptr() as *const c_char, tm);
12120                if r.is_null() { return core::ptr::null_mut(); }
12121                p = r as *const u8;
12122            }
12123            b's' => {
12124                let neg = if *p == b'-' { p = p.add(1); true } else { false };
12125                if *p < b'0' || *p > b'9' { return core::ptr::null_mut(); }
12126                let mut val = 0i64;
12127                while *p >= b'0' && *p <= b'9' {
12128                    val = val * 10 + (*p - b'0') as i64;
12129                    p = p.add(1);
12130                }
12131                if neg { val = -val; }
12132                secs_to_tm(val, &mut *tm);
12133            }
12134            b'S' => {
12135                let (v, np) = parse_range(p, 0, 60);
12136                if np == p { return core::ptr::null_mut(); }
12137                (*tm).tm_sec = v;
12138                p = np;
12139            }
12140            b'T' => {
12141                let r = strptime(p as *const c_char, b"%H:%M:%S\0".as_ptr() as *const c_char, tm);
12142                if r.is_null() { return core::ptr::null_mut(); }
12143                p = r as *const u8;
12144            }
12145            b'U' | b'W' => {
12146                let (_, np) = parse_range(p, 0, 53);
12147                if np == p { return core::ptr::null_mut(); }
12148                p = np;
12149            }
12150            b'V' => {
12151                let (_, np) = parse_range(p, 1, 53);
12152                if np == p { return core::ptr::null_mut(); }
12153                p = np;
12154            }
12155            b'w' => {
12156                let (v, np) = parse_range(p, 0, 6);
12157                if np == p { return core::ptr::null_mut(); }
12158                (*tm).tm_wday = v;
12159                p = np;
12160            }
12161            b'y' => {
12162                let (v, np) = parse_int(p, 2);
12163                if np == p { return core::ptr::null_mut(); }
12164                relyear = v;
12165                want_century |= 1;
12166                p = np;
12167            }
12168            b'Y' => {
12169                let neg = if *p == b'-' { p = p.add(1); true } else { false };
12170                let (v, np) = parse_int(p, 4);
12171                if np == p { return core::ptr::null_mut(); }
12172                (*tm).tm_year = if neg { -(v + 1900) } else { v - 1900 };
12173                want_century = 0;
12174                p = np;
12175            }
12176            b'z' => {
12177                if *p != b'+' && *p != b'-' { return core::ptr::null_mut(); }
12178                let neg = *p == b'-';
12179                p = p.add(1);
12180                let mut off = 0i64;
12181                let mut digits = 0;
12182                while digits < 4 && *p >= b'0' && *p <= b'9' {
12183                    off = off * 10 + (*p - b'0') as i64;
12184                    p = p.add(1);
12185                    digits += 1;
12186                }
12187                if digits == 0 { return core::ptr::null_mut(); }
12188                let (hh, mm) = if digits <= 2 { (off, 0i64) } else { (off / 100, off % 100) };
12189                (*tm).tm_gmtoff = (hh * 3600 + mm * 60) * if neg { -1 } else { 1 };
12190            }
12191            b'%' => {
12192                if *p != b'%' { return core::ptr::null_mut(); }
12193                p = p.add(1);
12194            }
12195            _ => { return core::ptr::null_mut(); }
12196        }
12197        f = f.add(1);
12198    }
12199    if want_century != 0 {
12200        (*tm).tm_year = relyear;
12201        if want_century & 2 != 0 { (*tm).tm_year += century * 100 - 1900; }
12202        else if (*tm).tm_year <= 68 { (*tm).tm_year += 100; }
12203    }
12204    p as *mut c_char
12205}
12206
12207// ============================================================
12208// Time: tzset / daylight / timezone / tzname
12209// ============================================================
12210
12211#[no_mangle]
12212pub static mut daylight: c_int = 0;
12213#[no_mangle]
12214pub static mut timezone: c_long = 0;
12215#[no_mangle]
12216pub static mut tzname: [*mut c_char; 2] = [b"UTC\0".as_ptr() as *mut c_char, b"UTC\0".as_ptr() as *mut c_char];
12217
12218#[no_mangle]
12219pub unsafe extern "C" fn tzset() {
12220    daylight = 0;
12221    timezone = 0;
12222}
12223
12224// ============================================================
12225// system()
12226// ============================================================
12227
12228#[no_mangle]
12229pub unsafe extern "C" fn system(cmd: *const c_char) -> c_int {
12230    if cmd.is_null() {
12231        // ponytail: test shell availability via fork+exec
12232        let pid = sys_fork();
12233        if pid == 0 {
12234            let sh = b"/bin/sh\0".as_ptr() as *const c_char;
12235            let dash_c = b"-c\0".as_ptr() as *const c_char;
12236            let exit0 = b"exit 0\0".as_ptr() as *const c_char;
12237            let argv = [b"sh\0".as_ptr() as *const c_char, dash_c, exit0, core::ptr::null()];
12238            sys_execve(sh, argv.as_ptr(), __environ as *const *const c_char);
12239            _exit(127);
12240        }
12241        if pid < 0 { return -1; }
12242        let mut status: c_int = 0;
12243        loop {
12244            let r = sys_wait4(pid as c_int, &mut status, 0, core::ptr::null_mut());
12245            if r >= 0 { return status; }
12246            if r != -4 { return -1; } // not EINTR
12247        }
12248    }
12249
12250    let sa_ignore = KSigAction {
12251        sa_handler: SIG_IGN,
12252        sa_flags: SA_RESTORER,
12253        sa_restorer: sig_restorer as *const () as usize,
12254        sa_mask: [0],
12255    };
12256    let mut oldint: KSigAction = core::mem::zeroed();
12257    let mut oldquit: KSigAction = core::mem::zeroed();
12258
12259    sys_rt_sigaction(SIGINT, &sa_ignore, &mut oldint, 8);
12260    sys_rt_sigaction(SIGQUIT, &sa_ignore, &mut oldquit, 8);
12261
12262    let mut block_set: SigSetT = 0;
12263    block_set |= 1u64 << (SIGCHLD - 1);
12264    let mut oldmask: SigSetT = 0;
12265    sys_rt_sigprocmask(SIG_BLOCK, &block_set, &mut oldmask, 8);
12266
12267    let pid = sys_fork();
12268
12269    if pid == 0 {
12270        sys_rt_sigaction(SIGINT, &oldint, core::ptr::null_mut(), 8);
12271        sys_rt_sigaction(SIGQUIT, &oldquit, core::ptr::null_mut(), 8);
12272        sys_rt_sigprocmask(SIG_SETMASK, &oldmask, core::ptr::null_mut(), 8);
12273
12274        let sh = b"/bin/sh\0".as_ptr() as *const c_char;
12275        let argv = [
12276            b"sh\0".as_ptr() as *const c_char,
12277            b"-c\0".as_ptr() as *const c_char,
12278            cmd,
12279            core::ptr::null(),
12280        ];
12281        sys_execve(sh, argv.as_ptr(), __environ as *const *const c_char);
12282        _exit(127);
12283    }
12284
12285    let mut status: c_int = -1;
12286    if pid > 0 {
12287        loop {
12288            let r = sys_wait4(pid as c_int, &mut status, 0, core::ptr::null_mut());
12289            if r >= 0 { break; }
12290            if r != -4 { break; } // not EINTR
12291        }
12292    }
12293
12294    sys_rt_sigaction(SIGINT, &oldint, core::ptr::null_mut(), 8);
12295    sys_rt_sigaction(SIGQUIT, &oldquit, core::ptr::null_mut(), 8);
12296    sys_rt_sigprocmask(SIG_SETMASK, &oldmask, core::ptr::null_mut(), 8);
12297
12298    status
12299}
12300
12301// ============================================================
12302// errno: strerror / strerror_r / environ alias
12303// ============================================================
12304
12305const EPERM_VAL: c_int = 1;
12306const ENOENT_VAL: c_int = 2;
12307const ESRCH_VAL: c_int = 3;
12308const EINTR_VAL: c_int = 4;
12309const EIO_VAL: c_int = 5;
12310const ENXIO_VAL: c_int = 6;
12311const E2BIG_VAL: c_int = 7;
12312const ENOEXEC_VAL: c_int = 8;
12313const EBADF_VAL: c_int = 9;
12314const ECHILD_VAL: c_int = 10;
12315const EAGAIN_VAL: c_int = 11;
12316const ENOMEM_VAL: c_int = 12;
12317const EACCES_VAL: c_int = 13;
12318const EFAULT_VAL: c_int = 14;
12319const ENOTBLK_VAL: c_int = 15;
12320const EBUSY_VAL: c_int = 16;
12321const EEXIST_VAL: c_int = 17;
12322const EXDEV_VAL: c_int = 18;
12323const ENODEV_VAL: c_int = 19;
12324const ENOTDIR_VAL: c_int = 20;
12325const EISDIR_VAL: c_int = 21;
12326const EINVAL_VAL: c_int = 22;
12327const ENFILE_VAL: c_int = 23;
12328const EMFILE_VAL: c_int = 24;
12329const ENOTTY_VAL: c_int = 25;
12330const ETXTBSY_VAL: c_int = 26;
12331const EFBIG_VAL: c_int = 27;
12332const ENOSPC_VAL: c_int = 28;
12333const ESPIPE_VAL: c_int = 29;
12334const EROFS_VAL: c_int = 30;
12335const EMLINK_VAL: c_int = 31;
12336const EPIPE_VAL: c_int = 32;
12337const EDOM_VAL: c_int = 33;
12338const ERANGE_VAL: c_int = 34;
12339const EDEADLK_VAL: c_int = 35;
12340const ENAMETOOLONG_VAL: c_int = 36;
12341const ENOLCK_VAL: c_int = 37;
12342const ENOSYS_VAL: c_int = 38;
12343const ENOTEMPTY_VAL: c_int = 39;
12344const ELOOP_VAL: c_int = 40;
12345const EWOULDBLOCK_VAL: c_int = 11;
12346const ENOMSG_VAL: c_int = 42;
12347const EIDRM_VAL: c_int = 43;
12348const ECHRNG_VAL: c_int = 44;
12349const EL2NSYNC_VAL: c_int = 45;
12350const EL3HLT_VAL: c_int = 46;
12351const EL3RST_VAL: c_int = 47;
12352const ELNRNG_VAL: c_int = 48;
12353const EUNATCH_VAL: c_int = 49;
12354const ENOCSI_VAL: c_int = 50;
12355const EL2HLT_VAL: c_int = 51;
12356const EBADE_VAL: c_int = 52;
12357const EBADR_VAL: c_int = 53;
12358const EXFULL_VAL: c_int = 54;
12359const ENOANO_VAL: c_int = 55;
12360const EBADRQC_VAL: c_int = 56;
12361const EBADSLT_VAL: c_int = 57;
12362const EBFONT_VAL: c_int = 59;
12363const ENOSTR_VAL: c_int = 60;
12364const ENODATA_VAL: c_int = 61;
12365const ETIME_VAL: c_int = 62;
12366const ENOSR_VAL: c_int = 63;
12367const ENONET_VAL: c_int = 64;
12368const ENOPKG_VAL: c_int = 65;
12369const EREMOTE_VAL: c_int = 66;
12370const ENOLINK_VAL: c_int = 67;
12371const EADV_VAL: c_int = 68;
12372const ESRMNT_VAL: c_int = 69;
12373const ECOMM_VAL: c_int = 70;
12374const EPROTO_VAL: c_int = 71;
12375const EMULTIHOP_VAL: c_int = 72;
12376const EDOTDOT_VAL: c_int = 73;
12377const EBADMSG_VAL: c_int = 74;
12378const EOVERFLOW_VAL: c_int = 75;
12379const ENOTUNIQ_VAL: c_int = 76;
12380const EBADFD_VAL: c_int = 77;
12381const EREMCHG_VAL: c_int = 78;
12382const ELIBACC_VAL: c_int = 79;
12383const ELIBBAD_VAL: c_int = 80;
12384const ELIBSCN_VAL: c_int = 81;
12385const ELIBMAX_VAL: c_int = 82;
12386const ELIBEXEC_VAL: c_int = 83;
12387const EILSEQ_VAL: c_int = 84;
12388const ERESTART_VAL: c_int = 85;
12389const ESTRPIPE_VAL: c_int = 86;
12390const EUSERS_VAL: c_int = 87;
12391const ENOTSOCK_VAL: c_int = 88;
12392const EDESTADDRREQ_VAL: c_int = 89;
12393const EMSGSIZE_VAL: c_int = 90;
12394const EPROTOTYPE_VAL: c_int = 91;
12395const ENOPROTOOPT_VAL: c_int = 92;
12396const EPROTONOSUPPORT_VAL: c_int = 93;
12397const ESOCKTNOSUPPORT_VAL: c_int = 94;
12398const EOPNOTSUPP_VAL: c_int = 95;
12399const ENOTSUP_VAL: c_int = 95;
12400const EPFNOSUPPORT_VAL: c_int = 96;
12401const EAFNOSUPPORT_VAL: c_int = 97;
12402const EADDRINUSE_VAL: c_int = 98;
12403const EADDRNOTAVAIL_VAL: c_int = 99;
12404const ENETDOWN_VAL: c_int = 100;
12405const ENETUNREACH_VAL: c_int = 101;
12406const ENETRESET_VAL: c_int = 102;
12407const ECONNABORTED_VAL: c_int = 103;
12408const ECONNRESET_VAL: c_int = 104;
12409const ENOBUFS_VAL: c_int = 105;
12410const EISCONN_VAL: c_int = 106;
12411const ENOTCONN_VAL: c_int = 107;
12412const ESHUTDOWN_VAL: c_int = 108;
12413const ETOOMANYREFS_VAL: c_int = 109;
12414const ETIMEDOUT_VAL: c_int = 110;
12415const ECONNREFUSED_VAL: c_int = 111;
12416const EHOSTDOWN_VAL: c_int = 112;
12417const EHOSTUNREACH_VAL: c_int = 113;
12418const EALREADY_VAL: c_int = 114;
12419const EINPROGRESS_VAL: c_int = 115;
12420const ESTALE_VAL: c_int = 116;
12421const EUCLEAN_VAL: c_int = 117;
12422const ENOTNAM_VAL: c_int = 118;
12423const ENAVAIL_VAL: c_int = 119;
12424const EISNAM_VAL: c_int = 120;
12425const EREMOTEIO_VAL: c_int = 121;
12426const EDQUOT_VAL: c_int = 122;
12427const ENOMEDIUM_VAL: c_int = 123;
12428const EMEDIUMTYPE_VAL: c_int = 124;
12429const ECANCELED_VAL: c_int = 125;
12430const ENOKEY_VAL: c_int = 126;
12431const EKEYEXPIRED_VAL: c_int = 127;
12432const EKEYREVOKED_VAL: c_int = 128;
12433const EKEYREJECTED_VAL: c_int = 129;
12434const EOWNERDEAD_VAL: c_int = 130;
12435const ENOTRECOVERABLE_VAL: c_int = 131;
12436const ERFKILL_VAL: c_int = 132;
12437const EHWPOISON_VAL: c_int = 133;
12438
12439// ponytail: static error string table indexed by errno
12440static ERR_STRS: [&[u8]; 134] = [
12441    b"Success\0",                     // 0
12442    b"Operation not permitted\0",     // 1
12443    b"No such file or directory\0",   // 2
12444    b"No such process\0",             // 3
12445    b"Interrupted system call\0",     // 4
12446    b"Input/output error\0",          // 5
12447    b"No such device or address\0",   // 6
12448    b"Argument list too long\0",      // 7
12449    b"Exec format error\0",           // 8
12450    b"Bad file descriptor\0",         // 9
12451    b"No child processes\0",          // 10
12452    b"Resource temporarily unavailable\0", // 11
12453    b"Cannot allocate memory\0",      // 12
12454    b"Permission denied\0",           // 13
12455    b"Bad address\0",                 // 14
12456    b"Block device required\0",       // 15
12457    b"Device or resource busy\0",     // 16
12458    b"File exists\0",                 // 17
12459    b"Invalid cross-device link\0",   // 18
12460    b"No such device\0",              // 19
12461    b"Not a directory\0",             // 20
12462    b"Is a directory\0",              // 21
12463    b"Invalid argument\0",            // 22
12464    b"Too many open files in system\0", // 23
12465    b"Too many open files\0",         // 24
12466    b"Inappropriate ioctl for device\0", // 25
12467    b"Text file busy\0",              // 26
12468    b"File too large\0",              // 27
12469    b"No space left on device\0",     // 28
12470    b"Illegal seek\0",                // 29
12471    b"Read-only file system\0",       // 30
12472    b"Too many links\0",              // 31
12473    b"Broken pipe\0",                 // 32
12474    b"Numerical argument out of domain\0", // 33
12475    b"Numerical result out of range\0", // 34
12476    b"Resource deadlock avoided\0",   // 35
12477    b"File name too long\0",          // 36
12478    b"No locks available\0",          // 37
12479    b"Function not implemented\0",    // 38
12480    b"Directory not empty\0",         // 39
12481    b"Too many levels of symbolic links\0", // 40
12482    b"Unknown error 41\0",            // 41
12483    b"No message of desired type\0",  // 42
12484    b"Identifier removed\0",          // 43
12485    b"Channel number out of range\0", // 44
12486    b"Level 2 not synchronized\0",    // 45
12487    b"Level 3 halted\0",              // 46
12488    b"Level 3 reset\0",               // 47
12489    b"Link number out of range\0",    // 48
12490    b"Protocol driver not attached\0", // 49
12491    b"No CSI structure available\0",  // 50
12492    b"Level 2 halted\0",              // 51
12493    b"Invalid exchange\0",            // 52
12494    b"Invalid request descriptor\0",  // 53
12495    b"Exchange full\0",               // 54
12496    b"No anode\0",                    // 55
12497    b"Invalid request code\0",        // 56
12498    b"Invalid slot\0",                // 57
12499    b"Unknown error 58\0",            // 58
12500    b"Bad font file format\0",        // 59
12501    b"Device not a stream\0",         // 60
12502    b"No data available\0",           // 61
12503    b"Timer expired\0",               // 62
12504    b"Out of streams resources\0",    // 63
12505    b"Machine is not on the network\0", // 64
12506    b"Package not installed\0",       // 65
12507    b"Object is remote\0",            // 66
12508    b"Link has been severed\0",       // 67
12509    b"Advertise error\0",             // 68
12510    b"Srmount error\0",               // 69
12511    b"Communication error on send\0", // 70
12512    b"Protocol error\0",              // 71
12513    b"Multihop attempted\0",          // 72
12514    b"RFS specific error\0",          // 73
12515    b"Bad message\0",                 // 74
12516    b"Value too large for defined data type\0", // 75
12517    b"Name not unique on network\0",  // 76
12518    b"File descriptor in bad state\0", // 77
12519    b"Remote address changed\0",      // 78
12520    b"Can not access a needed shared library\0", // 79
12521    b"Accessing a corrupted shared library\0", // 80
12522    b".lib section in a.out corrupted\0", // 81
12523    b"Attempting to link in too many shared libraries\0", // 82
12524    b"Cannot exec a shared library directly\0", // 83
12525    b"Invalid or incomplete multibyte or wide character\0", // 84
12526    b"Interrupted system call should be restarted\0", // 85
12527    b"Streams pipe error\0",          // 86
12528    b"Too many users\0",              // 87
12529    b"Socket operation on non-socket\0", // 88
12530    b"Destination address required\0", // 89
12531    b"Message too long\0",            // 90
12532    b"Protocol wrong type for socket\0", // 91
12533    b"Protocol not available\0",      // 92
12534    b"Protocol not supported\0",      // 93
12535    b"Socket type not supported\0",   // 94
12536    b"Operation not supported\0",     // 95
12537    b"Protocol family not supported\0", // 96
12538    b"Address family not supported by protocol\0", // 97
12539    b"Address already in use\0",      // 98
12540    b"Cannot assign requested address\0", // 99
12541    b"Network is down\0",             // 100
12542    b"Network is unreachable\0",      // 101
12543    b"Network dropped connection on reset\0", // 102
12544    b"Software caused connection abort\0", // 103
12545    b"Connection reset by peer\0",    // 104
12546    b"No buffer space available\0",   // 105
12547    b"Transport endpoint is already connected\0", // 106
12548    b"Transport endpoint is not connected\0", // 107
12549    b"Cannot send after transport endpoint shutdown\0", // 108
12550    b"Too many references: cannot splice\0", // 109
12551    b"Connection timed out\0",        // 110
12552    b"Connection refused\0",          // 111
12553    b"Host is down\0",                // 112
12554    b"No route to host\0",            // 113
12555    b"Operation already in progress\0", // 114
12556    b"Operation now in progress\0",   // 115
12557    b"Stale file handle\0",           // 116
12558    b"Structure needs cleaning\0",    // 117
12559    b"Not a XENIX named type file\0", // 118
12560    b"No XENIX semaphores available\0", // 119
12561    b"Is a named type file\0",        // 120
12562    b"Remote I/O error\0",            // 121
12563    b"Quota exceeded\0",              // 122
12564    b"No medium found\0",             // 123
12565    b"Wrong medium type\0",           // 124
12566    b"Operation canceled\0",          // 125
12567    b"Required key not available\0",  // 126
12568    b"Key has expired\0",             // 127
12569    b"Key has been revoked\0",        // 128
12570    b"Key was rejected by service\0", // 129
12571    b"Owner died\0",                  // 130
12572    b"State not recoverable\0",       // 131
12573    b"Operation not possible due to RF-kill\0", // 132
12574    b"Memory page has hardware error\0", // 133
12575];
12576
12577#[no_mangle]
12578pub unsafe extern "C" fn strerror(errnum: c_int) -> *mut c_char {
12579    if errnum < 0 || errnum as usize >= ERR_STRS.len() {
12580        // ponytail: static buf for unknown errors
12581        static mut UNKNOWN_BUF: [c_char; 32] = [0; 32];
12582        let prefix = b"Unknown error \0";
12583        let mut i = 0;
12584        while prefix[i] != 0 {
12585            UNKNOWN_BUF[i] = prefix[i] as c_char;
12586            i += 1;
12587        }
12588        let (buf, len) = format_u64(errnum as u64);
12589        for j in 0..len {
12590            UNKNOWN_BUF[i] = buf[20 - len + j] as c_char;
12591            i += 1;
12592        }
12593        UNKNOWN_BUF[i] = 0;
12594        return core::ptr::addr_of_mut!(UNKNOWN_BUF).cast::<c_char>();
12595    }
12596    ERR_STRS[errnum as usize].as_ptr() as *mut c_char
12597}
12598
12599#[no_mangle]
12600pub unsafe extern "C" fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: usize) -> c_int {
12601    let s = strerror(errnum);
12602    let slen = strlen(s as *const c_char);
12603    if slen >= buflen {
12604        if buflen > 0 {
12605            core::ptr::copy_nonoverlapping(s as *const u8, buf as *mut u8, buflen - 1);
12606            *buf.add(buflen - 1) = 0;
12607        }
12608        return 34; // ERANGE
12609    }
12610    core::ptr::copy_nonoverlapping(s as *const u8, buf as *mut u8, slen + 1);
12611    0
12612}
12613
12614// ponytail: `environ` alias for tests that use extern char **environ
12615#[no_mangle]
12616pub static mut environ: *mut *mut c_char = core::ptr::null_mut();
12617
12618// keep environ in sync with __environ
12619unsafe fn sync_environ() {
12620    environ = __environ;
12621}
12622
12623// ============================================================
12624// unistd extensions: pipe, dup, dup2, fcntl, access, unlink, etc.
12625// ============================================================
12626
12627#[inline]
12628unsafe fn sys_dup(oldfd: i32) -> i64 {
12629    <Arch as Syscalls>::syscall1(SYS_DUP, oldfd as i64)
12630}
12631
12632#[inline]unsafe fn sys_dup2(oldfd: i32, newfd: i32) -> i64 {
12633    // ponytail: dup3 with flags=0 is equivalent to dup2
12634    sys_dup3(oldfd, newfd, 0)
12635}
12636
12637#[inline]
12638unsafe fn sys_access(path: *const u8, mode: i32) -> i64 {
12639    <Arch as Syscalls>::syscall4(SYS_FACCESSAT, AT_FDCWD as i64, path as i64, mode as i64, 0)
12640}
12641
12642#[inline]unsafe fn sys_unlink(path: *const u8) -> i64 {
12643    sys_unlinkat(AT_FDCWD, path, 0)
12644}
12645
12646#[inline]unsafe fn sys_rmdir(path: *const u8) -> i64 {
12647    sys_unlinkat(AT_FDCWD, path, 512) // AT_REMOVEDIR
12648}
12649
12650#[inline]
12651unsafe fn sys_chdir(path: *const u8) -> i64 {
12652    <Arch as Syscalls>::syscall1(SYS_CHDIR, path as i64)
12653}
12654
12655#[inline]
12656unsafe fn sys_getcwd(buf: *mut u8, size: usize) -> i64 {
12657    <Arch as Syscalls>::syscall2(SYS_GETCWD, buf as i64, size as i64)
12658}
12659
12660#[inline]
12661unsafe fn sys_sethostname(name: *const u8, len: usize) -> i64 {
12662    <Arch as Syscalls>::syscall2(SYS_SETHOSTNAME, name as i64, len as i64)
12663}
12664
12665#[inline]
12666unsafe fn sys_gethostname(buf: *mut u8, len: usize) -> i64 {
12667    #[repr(C)]
12668    struct UtsName {
12669        sysname: [u8; 65],
12670        nodename: [u8; 65],
12671        release: [u8; 65],
12672        version: [u8; 65],
12673        machine: [u8; 65],
12674        domainname: [u8; 65],
12675    }
12676    let mut uts: UtsName = core::mem::zeroed();
12677    let result = <Arch as Syscalls>::syscall1(SYS_UNAME, &mut uts as *mut UtsName as i64);
12678    if result < 0 {
12679        return result;
12680    }
12681    let nlen = strlen(uts.nodename.as_ptr() as *const c_char);
12682    let copylen = if nlen < len { nlen } else { if len > 0 { len - 1 } else { 0 } };
12683    core::ptr::copy_nonoverlapping(uts.nodename.as_ptr(), buf, copylen);
12684    if len > 0 { *buf.add(copylen) = 0; }
12685    0
12686}
12687
12688unsafe fn sys_truncate(path: *const u8, length: i64) -> i64 {
12689    <Arch as Syscalls>::syscall2(SYS_TRUNCATE, path as i64, length as i64)
12690}
12691
12692unsafe fn sys_ftruncate(fd: i32, length: i64) -> i64 {
12693    <Arch as Syscalls>::syscall2(SYS_FTRUNCATE, fd as i64, length as i64)
12694}
12695
12696unsafe fn sys_nanosleep(req: *const timespec, rem: *mut timespec) -> i64 {
12697    <Arch as Syscalls>::syscall2(SYS_NANOSLEEP, req as i64, rem as i64)
12698}
12699
12700const ITIMER_REAL: i32 = 0;
12701
12702#[repr(C)]
12703struct itimerval {
12704    it_interval: timeval,
12705    it_value: timeval,
12706}
12707
12708unsafe fn sys_alarm(seconds: c_uint) -> i64 {
12709    let mut new: itimerval = core::mem::zeroed();
12710    new.it_value.tv_sec = seconds as c_long;
12711    let mut old: itimerval = core::mem::zeroed();
12712    let r = <Arch as Syscalls>::syscall3(
12713        SYS_SETITIMER,
12714        ITIMER_REAL as i64,
12715        &new as *const _ as i64,
12716        &mut old as *mut _ as i64,
12717    );
12718    if r < 0 {
12719        return r;
12720    }
12721    old.it_value.tv_sec as i64 + (old.it_value.tv_usec != 0) as i64
12722}
12723
12724unsafe fn sys_pause() -> i64 {
12725    #[cfg(target_arch = "x86_64")]
12726    { <Arch as Syscalls>::syscall0(SYS_PAUSE) }
12727    #[cfg(target_arch = "aarch64")]
12728    { <Arch as Syscalls>::syscall4(SYS_PPOLL, 0, 0, 0, 0) }
12729    #[cfg(target_arch = "riscv64")]
12730    { <Arch as Syscalls>::syscall4(SYS_PPOLL, 0, 0, 0, 0) }
12731}
12732
12733unsafe fn sys_fsync(fd: i32) -> i64 {
12734    <Arch as Syscalls>::syscall1(SYS_FSYNC, fd as i64)
12735}
12736
12737unsafe fn sys_fdatasync(fd: i32) -> i64 {
12738    <Arch as Syscalls>::syscall1(SYS_FDATASYNC, fd as i64)
12739}
12740
12741unsafe fn sys_sync() {
12742    let _ = <Arch as Syscalls>::syscall0(SYS_SYNC);
12743}
12744
12745unsafe fn sys_symlink(target: *const u8, linkpath: *const u8) -> i64 {
12746    <Arch as Syscalls>::syscall3(SYS_SYMLINKAT, target as i64, AT_FDCWD as i64, linkpath as i64)
12747}
12748
12749unsafe fn sys_symlinkat(target: *const u8, newdirfd: i32, linkpath: *const u8) -> i64 {
12750    <Arch as Syscalls>::syscall3(SYS_SYMLINKAT, target as i64, newdirfd as i64, linkpath as i64)
12751}
12752
12753unsafe fn sys_readlinkat(dirfd: i32, path: *const u8, buf: *mut u8, bufsiz: usize) -> i64 {
12754    <Arch as Syscalls>::syscall4(SYS_READLINKAT, dirfd as i64, path as i64, buf as i64, bufsiz as i64)
12755}
12756
12757unsafe fn sys_linkat(olddirfd: i32, oldpath: *const u8, newdirfd: i32, newpath: *const u8, flags: i32) -> i64 {
12758    <Arch as Syscalls>::syscall5(SYS_LINKAT, olddirfd as i64, oldpath as i64, newdirfd as i64, newpath as i64, flags as i64)
12759}
12760
12761unsafe fn sys_fchmod(fd: i32, mode: u32) -> i64 {
12762    <Arch as Syscalls>::syscall2(SYS_FCHMOD, fd as i64, mode as i64)
12763}
12764
12765unsafe fn sys_fchmodat(dirfd: i32, path: *const u8, mode: u32, flags: i32) -> i64 {
12766    <Arch as Syscalls>::syscall4(SYS_FCHMODAT, dirfd as i64, path as i64, mode as i64, flags as i64)
12767}
12768
12769unsafe fn sys_umask(mask: u32) -> u32 {
12770    <Arch as Syscalls>::syscall1(SYS_UMASK, mask as i64) as u32
12771}
12772
12773unsafe fn sys_getgroups(size: i32, list: *mut c_uint) -> i64 {
12774    <Arch as Syscalls>::syscall2(SYS_GETGROUPS, size as i64, list as i64)
12775}
12776
12777unsafe fn sys_setuid(uid: c_uint) -> i64 {
12778    <Arch as Syscalls>::syscall1(SYS_SETUID, uid as i64)
12779}
12780
12781unsafe fn sys_setgid(gid: c_uint) -> i64 {
12782    <Arch as Syscalls>::syscall1(SYS_SETGID, gid as i64)
12783}
12784
12785unsafe fn sys_setsid() -> i64 {
12786    <Arch as Syscalls>::syscall0(SYS_SETSID)
12787}
12788
12789unsafe fn sys_setpgid(pid: c_int, pgid: c_int) -> i64 {
12790    <Arch as Syscalls>::syscall2(SYS_SETPGID, pid as i64, pgid as i64)
12791}
12792
12793unsafe fn sys_getpgid(pid: c_int) -> i64 {
12794    <Arch as Syscalls>::syscall1(SYS_GETPGID, pid as i64)
12795}
12796
12797unsafe fn sys_getsid(pid: c_int) -> i64 {
12798    <Arch as Syscalls>::syscall1(SYS_GETSID, pid as i64)
12799}
12800
12801unsafe fn sys_mkdirat(dirfd: i32, path: *const u8, mode: u32) -> i64 {
12802    <Arch as Syscalls>::syscall3(SYS_MKDIRAT, dirfd as i64, path as i64, mode as i64)
12803}
12804
12805// Public C ABI wrappers for unistd
12806
12807#[no_mangle]
12808pub unsafe extern "C" fn pipe(fds: *mut c_int) -> c_int {
12809    let r = sys_pipe2(fds, 0);
12810    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
12811    0
12812}
12813
12814#[no_mangle]
12815pub unsafe extern "C" fn pipe2(fds: *mut c_int, flags: c_int) -> c_int {
12816    let r = sys_pipe2(fds, flags);
12817    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
12818    0
12819}
12820
12821#[no_mangle]
12822pub unsafe extern "C" fn dup(oldfd: c_int) -> c_int {
12823    let r = sys_dup(oldfd);
12824    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
12825    r as c_int
12826}
12827
12828#[no_mangle]
12829pub unsafe extern "C" fn dup2(oldfd: c_int, newfd: c_int) -> c_int {
12830    if oldfd == newfd {
12831        // dup3 rejects equal descriptors with EINVAL; dup2 must validate the
12832        // descriptor and return newfd unchanged.
12833        let r = sys_fcntl(oldfd, 1 /* F_GETFD */, 0);
12834        if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
12835        return newfd;
12836    }
12837    let r = sys_dup2(oldfd, newfd);
12838    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
12839    r as c_int
12840}
12841
12842#[no_mangle]
12843pub unsafe extern "C" fn dup3(oldfd: c_int, newfd: c_int, flags: c_int) -> c_int {
12844    let r = sys_dup3(oldfd, newfd, flags);
12845    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
12846    r as c_int
12847}
12848
12849#[no_mangle]
12850pub unsafe extern "C" fn fcntl(fd: c_int, cmd: c_int, mut args: ...) -> c_int {
12851    // Commands without an argument. Everything else (including seals,
12852    // pipe sizes, ownership and locks) takes the third argument.
12853    let no_arg = matches!(cmd, 1 /* F_GETFD */ | 3 /* F_GETFL */ | 9 /* F_GETOWN */
12854        | 11 /* F_GETSIG */ | 1025 /* F_GETLEASE */ | 1032 /* F_GETPIPE_SZ */
12855        | 1034 /* F_GET_SEALS */);
12856    let r = if no_arg {
12857        sys_fcntl(fd, cmd, 0)
12858    } else {
12859        let arg = args.next_arg::<c_long>();
12860        sys_fcntl(fd, cmd, arg)
12861    };
12862    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
12863    r as c_int
12864}
12865
12866#[no_mangle]
12867pub unsafe extern "C" fn access(path: *const c_char, mode: c_int) -> c_int {
12868    let r = sys_access(path as *const u8, mode);
12869    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
12870    0
12871}
12872
12873#[no_mangle]
12874pub unsafe extern "C" fn unlink(path: *const c_char) -> c_int {
12875    let r = sys_unlink(path as *const u8);
12876    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
12877    0
12878}
12879
12880#[no_mangle]
12881pub unsafe extern "C" fn rmdir(path: *const c_char) -> c_int {
12882    let r = sys_rmdir(path as *const u8);
12883    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
12884    0
12885}
12886
12887#[no_mangle]
12888pub unsafe extern "C" fn chdir(path: *const c_char) -> c_int {
12889    let r = sys_chdir(path as *const u8);
12890    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
12891    0
12892}
12893
12894#[no_mangle]
12895pub unsafe extern "C" fn getcwd(buf: *mut c_char, size: usize) -> *mut c_char {
12896    if buf.is_null() {
12897        // allocate
12898        let alloc_size = if size == 0 { 256 } else { size };
12899        let p = malloc(alloc_size) as *mut c_char;
12900        if p.is_null() { ERRNO!() = ENOMEM; return core::ptr::null_mut(); }
12901        let r = sys_getcwd(p as *mut u8, alloc_size);
12902        if r < 0 { free(p as *mut c_void); ERRNO!() = (-r) as c_int; return core::ptr::null_mut(); }
12903        // The kernel returns "(unreachable)..." when cwd is outside the
12904        // current root; POSIX getcwd must fail with ENOENT in that case.
12905        if *p as u8 != b'/' {
12906            free(p as *mut c_void);
12907            ERRNO!() = 2;
12908            return core::ptr::null_mut();
12909        }
12910        return p;
12911    }
12912    if size == 0 { ERRNO!() = EINVAL; return core::ptr::null_mut(); }
12913    let r = sys_getcwd(buf as *mut u8, size);
12914    if r < 0 { ERRNO!() = (-r) as c_int; return core::ptr::null_mut(); }
12915    if *buf as u8 != b'/' {
12916        ERRNO!() = 2;
12917        return core::ptr::null_mut();
12918    }
12919    buf
12920}
12921
12922#[no_mangle]
12923pub unsafe extern "C" fn gethostname(name: *mut c_char, len: usize) -> c_int {
12924    let r = sys_gethostname(name as *mut u8, len);
12925    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
12926    0
12927}
12928
12929// ponytail: getpagesize - hardcoded 4096 for x86_64
12930#[no_mangle]
12931pub extern "C" fn getpagesize() -> c_int {
12932    4096
12933}
12934
12935#[no_mangle]
12936pub unsafe extern "C" fn truncate(path: *const c_char, length: i64) -> c_int {
12937    let r = sys_truncate(path as *const u8, length);
12938    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
12939    0
12940}
12941
12942#[no_mangle]
12943pub unsafe extern "C" fn ftruncate(fd: c_int, length: i64) -> c_int {
12944    let r = sys_ftruncate(fd, length);
12945    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
12946    0
12947}
12948
12949#[no_mangle]
12950pub unsafe extern "C" fn nanosleep(req: *const timespec, rem: *mut timespec) -> c_int {
12951    pthread_testcancel();
12952    let r = sys_nanosleep(req, rem);
12953    pthread_testcancel();
12954    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
12955    0
12956}
12957
12958#[no_mangle]
12959pub unsafe extern "C" fn sleep(seconds: c_uint) -> c_uint {
12960    let req = timespec { tv_sec: seconds as c_long, tv_nsec: 0 };
12961    let mut rem: timespec = core::mem::zeroed();
12962    pthread_testcancel();
12963    let r = sys_nanosleep(&req, &mut rem);
12964    pthread_testcancel();
12965    if r < 0 {
12966        let e = (-r) as c_int;
12967        if e == EINTR { return rem.tv_sec as c_uint; }
12968    }
12969    0
12970}
12971
12972#[no_mangle]
12973pub unsafe extern "C" fn usleep(usec: c_uint) -> c_int {
12974    let req = timespec { tv_sec: (usec / 1000000) as c_long, tv_nsec: ((usec % 1000000) * 1000) as c_long };
12975    pthread_testcancel();
12976    let r = sys_nanosleep(&req, core::ptr::null_mut());
12977    pthread_testcancel();
12978    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
12979    0
12980}
12981
12982#[no_mangle]
12983pub unsafe extern "C" fn alarm(seconds: c_uint) -> c_uint {
12984    // POSIX rounds a nonzero fractional remainder up to one second;
12985    // sys_alarm() already performs that rounding.
12986    let r = sys_alarm(seconds);
12987    if r < 0 { 0 } else { r as c_uint }
12988}
12989
12990#[no_mangle]
12991pub unsafe extern "C" fn pause() -> c_int {
12992    pthread_testcancel();
12993    sys_pause();
12994    pthread_testcancel();
12995    ERRNO!() = EINTR;
12996    -1
12997}
12998
12999#[no_mangle]
13000pub unsafe extern "C" fn fsync(fd: c_int) -> c_int {
13001    let r = sys_fsync(fd);
13002    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
13003    0
13004}
13005
13006#[no_mangle]
13007pub unsafe extern "C" fn fdatasync(fd: c_int) -> c_int {
13008    let r = sys_fdatasync(fd);
13009    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
13010    0
13011}
13012
13013#[no_mangle]
13014pub unsafe extern "C" fn sync() {
13015    sys_sync();
13016}
13017
13018#[no_mangle]
13019pub unsafe extern "C" fn symlink(target: *const c_char, linkpath: *const c_char) -> c_int {
13020    let r = sys_symlinkat(target as *const u8, AT_FDCWD, linkpath as *const u8);
13021    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
13022    0
13023}
13024
13025#[no_mangle]
13026pub unsafe extern "C" fn readlink(path: *const c_char, buf: *mut c_char, bufsiz: usize) -> isize {
13027    let r = sys_readlinkat(AT_FDCWD, path as *const u8, buf as *mut u8, bufsiz);
13028    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
13029    r as isize
13030}
13031
13032#[no_mangle]
13033pub unsafe extern "C" fn link(oldpath: *const c_char, newpath: *const c_char) -> c_int {
13034    let r = sys_linkat(AT_FDCWD, oldpath as *const u8, AT_FDCWD, newpath as *const u8, 0);
13035    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
13036    0
13037}
13038
13039#[no_mangle]
13040pub unsafe extern "C" fn chmod(path: *const c_char, mode: c_uint) -> c_int {
13041    let r = sys_fchmodat(AT_FDCWD, path as *const u8, mode, 0);
13042    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
13043    0
13044}
13045
13046#[no_mangle]
13047pub unsafe extern "C" fn fchmod(fd: c_int, mode: c_uint) -> c_int {
13048    let r = sys_fchmod(fd, mode);
13049    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
13050    0
13051}
13052
13053#[no_mangle]
13054pub unsafe extern "C" fn umask(mask: c_uint) -> c_uint {
13055    sys_umask(mask) as c_uint
13056}
13057
13058#[no_mangle]
13059pub unsafe extern "C" fn isatty(fd: c_int) -> c_int {
13060    let mut ws: winsize = core::mem::zeroed();
13061    if sys_ioctl(fd, TIOCGWINSZ, &mut ws as *mut winsize as *mut u8) == 0 { 1 } else { 0 }
13062}
13063
13064#[no_mangle]
13065pub unsafe extern "C" fn getlogin() -> *mut c_char {
13066    static mut LOGIN_BUF: [c_char; 32] = [0; 32];
13067    let pw = getpwuid(crate::getuid());
13068    if pw.is_null() { return core::ptr::null_mut(); }
13069    let n = crate::strlen((*pw).pw_name);
13070    if n + 1 > LOGIN_BUF.len() { return core::ptr::null_mut(); }
13071    core::ptr::copy_nonoverlapping((*pw).pw_name, LOGIN_BUF.as_mut_ptr(), n + 1);
13072    LOGIN_BUF.as_mut_ptr()
13073}
13074
13075#[no_mangle]
13076pub unsafe extern "C" fn getgroups(size: c_int, list: *mut c_uint) -> c_int {
13077    let r = sys_getgroups(size, list);
13078    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
13079    r as c_int
13080}
13081
13082#[no_mangle]
13083pub unsafe extern "C" fn setuid(uid: c_uint) -> c_int {
13084    let r = sys_setuid(uid);
13085    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
13086    0
13087}
13088
13089#[no_mangle]
13090pub unsafe extern "C" fn setgid(gid: c_uint) -> c_int {
13091    let r = sys_setgid(gid);
13092    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
13093    0
13094}
13095
13096#[no_mangle]
13097pub unsafe extern "C" fn seteuid(euid: c_uint) -> c_int {
13098    // Setting only the effective uid must keep the real and saved uids, so a
13099    // process can drop privileges and later restore them (setuid would clear
13100    // the saved uid for root).
13101    setresuid(u32::MAX, euid, u32::MAX)
13102}
13103
13104#[no_mangle]
13105pub unsafe extern "C" fn setegid(egid: c_uint) -> c_int {
13106    setresgid(u32::MAX, egid, u32::MAX)
13107}
13108
13109#[no_mangle]
13110pub unsafe extern "C" fn setreuid(ruid: c_uint, euid: c_uint) -> c_int {
13111    let r = <Arch as Syscalls>::syscall2(sysno(113, 145), ruid as i64, euid as i64);
13112    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
13113}
13114
13115
13116
13117#[no_mangle]
13118pub unsafe extern "C" fn setsid() -> c_int {
13119    let r = sys_setsid();
13120    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
13121    r as c_int
13122}
13123
13124#[no_mangle]
13125pub unsafe extern "C" fn setpgid(pid: c_int, pgid: c_int) -> c_int {
13126    let r = sys_setpgid(pid, pgid);
13127    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
13128    0
13129}
13130
13131#[no_mangle]
13132pub unsafe extern "C" fn getpgid(pid: c_int) -> c_int {
13133    let r = sys_getpgid(pid);
13134    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
13135    r as c_int
13136}
13137
13138#[no_mangle]
13139pub unsafe extern "C" fn getsid(pid: c_int) -> c_int {
13140    let r = sys_getsid(pid);
13141    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
13142    r as c_int
13143}
13144
13145#[no_mangle]
13146pub unsafe extern "C" fn getpgrp() -> c_int {
13147    sys_getpgid(0) as c_int
13148}
13149
13150#[no_mangle]
13151pub unsafe extern "C" fn setpgrp() -> c_int {
13152    setpgid(0, 0)
13153}
13154
13155#[no_mangle]
13156unsafe fn mkstemps_impl(template: *mut c_char, suffixlen: i32) -> c_int {
13157    if template.is_null() || suffixlen < 0 {
13158        ERRNO!() = EINVAL;
13159        return -1;
13160    }
13161    let len = strlen(template as *const c_char);
13162    let suffix = suffixlen as usize;
13163    if len < 6 + suffix {
13164        ERRNO!() = EINVAL;
13165        return -1;
13166    }
13167    let x_start = len - suffix - 6;
13168    for i in x_start..x_start + 6 {
13169        if *template.add(i) != b'X' as c_char {
13170            ERRNO!() = EINVAL;
13171            return -1;
13172        }
13173    }
13174    let mut ts: timespec = core::mem::zeroed();
13175    let _ = sys_clock_gettime(CLOCK_REALTIME, &mut ts);
13176    let mut ctr: u32 = ts.tv_nsec as u32;
13177    for _ in 0..100 {
13178        ctr = ctr.wrapping_mul(1103515245).wrapping_add(12345);
13179        let mut seed = ctr;
13180        let hex = b"0123456789abcdef";
13181        for i in x_start..x_start + 6 {
13182            *template.add(i) = hex[(seed & 0xf) as usize] as c_char;
13183            seed >>= 4;
13184        }
13185        let fd = sys_open(template as *const u8, (O_RDWR | O_CREAT | O_EXCL) as i64, 0o600);
13186        if fd >= 0 {
13187            return fd as c_int;
13188        }
13189        let e = (-fd) as c_int;
13190        if e != EEXIST_VAL {
13191            ERRNO!() = e;
13192            return -1;
13193        }
13194    }
13195    ERRNO!() = EEXIST_VAL;
13196    -1
13197}
13198
13199#[no_mangle]
13200pub unsafe extern "C" fn mkstemp(template: *mut c_char) -> c_int {
13201    mkstemps_impl(template, 0)
13202}
13203
13204#[no_mangle]
13205pub unsafe extern "C" fn mkdir(path: *const c_char, mode: c_uint) -> c_int {
13206    let r = sys_mkdirat(AT_FDCWD, path as *const u8, mode);
13207    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
13208    0
13209}
13210
13211// ============================================================
13212// libgen: basename / dirname
13213// ============================================================
13214
13215#[no_mangle]
13216pub unsafe extern "C" fn dirname(s: *mut c_char) -> *mut c_char {
13217    if s.is_null() || *s == 0 {
13218        return b".\0".as_ptr() as *mut c_char;
13219    }
13220    let mut i = strlen(s as *const c_char);
13221    i = i.wrapping_sub(1);
13222    // skip trailing slashes
13223    while i > 0 && *s.add(i) == b'/' as c_char {
13224        i = i.wrapping_sub(1);
13225    }
13226    if i == 0 && *s.add(0) == b'/' as c_char {
13227        return b"/\0".as_ptr() as *mut c_char;
13228    }
13229    // skip non-slashes
13230    while i > 0 && *s.add(i) != b'/' as c_char {
13231        i = i.wrapping_sub(1);
13232    }
13233    if i == 0 {
13234        if *s.add(0) == b'/' as c_char {
13235            return b"/\0".as_ptr() as *mut c_char;
13236        }
13237        return b".\0".as_ptr() as *mut c_char;
13238    }
13239    // skip slashes
13240    while i > 0 && *s.add(i - 1) == b'/' as c_char {
13241        i = i.wrapping_sub(1);
13242    }
13243    if i == 0 {
13244        return b"/\0".as_ptr() as *mut c_char;
13245    }
13246    *s.add(i) = 0;
13247    s
13248}
13249
13250#[no_mangle]
13251pub unsafe extern "C" fn basename(s: *mut c_char) -> *mut c_char {
13252    if s.is_null() || *s == 0 {
13253        return b".\0".as_ptr() as *mut c_char;
13254    }
13255    let mut i = strlen(s as *const c_char);
13256    i = i.wrapping_sub(1);
13257    // strip trailing slashes
13258    while i > 0 && *s.add(i) == b'/' as c_char {
13259        *s.add(i) = 0;
13260        i = i.wrapping_sub(1);
13261    }
13262    // find last slash
13263    while i > 0 && *s.add(i - 1) != b'/' as c_char {
13264        i = i.wrapping_sub(1);
13265    }
13266    s.add(i)
13267}
13268
13269// ============================================================
13270// arpa/inet: inet_pton / inet_ntop / inet_addr / inet_ntoa / inet_aton
13271// ============================================================
13272
13273const AF_INET_VAL: c_int = 2;
13274const AF_INET6_VAL: c_int = 10;
13275const EAFNOSUPPORT: c_int = 97;
13276const ENOSPC_VAL2: c_int = 28;
13277
13278unsafe fn inet_pton_v4(s: *const u8, a: *mut u8) -> c_int {
13279    let mut p = s;
13280    let mut i = 0;
13281    while i < 4 {
13282        let oct_start = p;
13283        let mut v: u32 = 0;
13284        let mut j: u32 = 0;
13285        while j < 3 && *p >= b'0' && *p <= b'9' {
13286            v = v * 10 + (*p - b'0') as u32;
13287            p = p.add(1);
13288            j += 1;
13289        }
13290        if j == 0 || (j > 1 && *oct_start == b'0') || v > 255 { return 0; }
13291        *a.add(i) = v as u8;
13292        if *p == 0 && i == 3 { return 1; }
13293        if *p != b'.' { return 0; }
13294        p = p.add(1);
13295        i += 1;
13296    }
13297    0
13298}
13299
13300unsafe fn inet_pton_v6(s: *const u8, a: *mut u8) -> c_int {
13301    let mut ip = [0u16; 8];
13302    let mut p = s;
13303    let mut i: usize = 0;
13304    let mut brk: isize = -1;
13305    let mut need_v4 = false;
13306    let mut v4_start: *const u8 = core::ptr::null();
13307
13308    if *p == b':' {
13309        p = p.add(1);
13310        if *p != b':' { return 0; }
13311    }
13312
13313    loop {
13314        if *p == b':' && brk < 0 {
13315            brk = i as isize;
13316            ip[i & 7] = 0;
13317            p = p.add(1);
13318            if *p == 0 { break; }
13319            if i == 7 { return 0; }
13320            i += 1;
13321            continue;
13322        }
13323        v4_start = p;
13324        let mut v: u16 = 0;
13325        let mut j = 0;
13326        while j < 4 {
13327            let d = hex_digit(*p);
13328            if d < 0 { break; }
13329            v = v * 16 + d as u16;
13330            p = p.add(1);
13331            j += 1;
13332        }
13333        if j == 0 { return 0; }
13334        ip[i & 7] = v;
13335        if *p == 0 && (brk >= 0 || i == 7) { break; }
13336        if i == 7 { return 0; }
13337        if *p != b':' {
13338            if *p != b'.' || (i < 6 && brk < 0) { return 0; }
13339            need_v4 = true;
13340            i += 1;
13341            ip[i & 7] = 0;
13342            break;
13343        }
13344        p = p.add(1);
13345        i += 1;
13346    }
13347
13348    if brk >= 0 {
13349        // expand :: : move trailing fields to the end and zero the gap
13350        let trail_count = i + 1 - brk as usize;
13351        let dest_start = brk as usize + 7 - i;
13352        // move backwards to avoid overwrite in the overlapping case
13353        for j in (0..trail_count).rev() {
13354            ip[dest_start + j] = ip[brk as usize + j];
13355        }
13356        for j in 0..(7 - i) {
13357            ip[brk as usize + j] = 0;
13358        }
13359    }
13360
13361    for k in 0..8 {
13362        *a.add(k * 2) = (ip[k] >> 8) as u8;
13363        *a.add(k * 2 + 1) = (ip[k] & 0xff) as u8;
13364    }
13365
13366    if need_v4 {
13367        if inet_pton_v4(v4_start, a.add(12)) <= 0 { return 0; }
13368    }
13369    1
13370}
13371
13372unsafe fn hex_digit(c: u8) -> i32 {
13373    if c >= b'0' && c <= b'9' { (c - b'0') as i32 }
13374    else if c >= b'a' && c <= b'f' { (c - b'a' + 10) as i32 }
13375    else if c >= b'A' && c <= b'F' { (c - b'A' + 10) as i32 }
13376    else { -1 }
13377}
13378
13379#[no_mangle]
13380pub unsafe extern "C" fn inet_pton(af: c_int, s: *const c_char, a: *mut c_void) -> c_int {
13381    if af == AF_INET_VAL {
13382        inet_pton_v4(s as *const u8, a as *mut u8)
13383    } else if af == AF_INET6_VAL {
13384        inet_pton_v6(s as *const u8, a as *mut u8)
13385    } else {
13386        ERRNO!() = EAFNOSUPPORT;
13387        -1
13388    }
13389}
13390
13391#[no_mangle]
13392pub unsafe extern "C" fn inet_ntop(af: c_int, a: *const c_void, s: *mut c_char, size: u32) -> *mut c_char {
13393    if af == AF_INET_VAL {
13394        let b = a as *const u8;
13395        let needed = 16; // "255.255.255.255\0"
13396        if size < needed as u32 { ERRNO!() = ENOSPC_VAL2; return core::ptr::null_mut(); }
13397        let mut pos = 0;
13398        for i in 0..4 {
13399            if i > 0 { *s.add(pos) = b'.' as c_char; pos += 1; }
13400            let (buf, len) = format_u64(*b.add(i) as u64);
13401            for j in 0..len { *s.add(pos) = buf[j] as c_char; pos += 1; }
13402        }
13403        *s.add(pos) = 0;
13404        s
13405    } else if af == AF_INET6_VAL {
13406        let b = a as *const u8;
13407        let needed = 46; // max IPv6
13408        if size < needed as u32 { ERRNO!() = ENOSPC_VAL2; return core::ptr::null_mut(); }
13409        let mut words = [0u16; 8];
13410        for i in 0..8 {
13411            words[i] = ((*b.add(i * 2) as u16) << 8) | (*b.add(i * 2 + 1) as u16);
13412        }
13413        // IPv4-mapped: ::ffff:a.b.c.d
13414        let mut pos = 0usize;
13415        if words[0] == 0 && words[1] == 0 && words[2] == 0 && words[3] == 0
13416            && words[4] == 0 && words[5] == 0xffff
13417        {
13418            let prefix = b"::ffff:";
13419            for k in 0..prefix.len() { *s.add(pos) = prefix[k] as c_char; pos += 1; }
13420            for idx in 0..4 {
13421                if idx > 0 { *s.add(pos) = b'.' as c_char; pos += 1; }
13422                let octet = *b.add(12 + idx);
13423                if octet >= 100 {
13424                    *s.add(pos) = (b'0' + octet / 100) as c_char; pos += 1;
13425                    *s.add(pos) = (b'0' + (octet / 10) % 10) as c_char; pos += 1;
13426                } else if octet >= 10 {
13427                    *s.add(pos) = (b'0' + octet / 10) as c_char; pos += 1;
13428                }
13429                *s.add(pos) = (b'0' + octet % 10) as c_char; pos += 1;
13430            }
13431            *s.add(pos) = 0;
13432            return s;
13433        }
13434        // find longest run of zeros
13435        let mut best_start: isize = -1;
13436        let mut best_len: usize = 0;
13437        let mut j: usize = 0;
13438        while j < 8 {
13439            if words[j] == 0 {
13440                let mut k = j;
13441                while k < 8 && words[k] == 0 { k += 1; }
13442                let run = k - j;
13443                if run > best_len { best_start = j as isize; best_len = run; }
13444                j = k;
13445            } else {
13446                j += 1;
13447            }
13448        }
13449        let mut i: usize = 0;
13450        let mut prev_in_run = false;
13451        while i < 8 {
13452            let in_run = best_len >= 2 && i >= best_start as usize && i < best_start as usize + best_len;
13453            if in_run {
13454                if i == best_start as usize {
13455                    *s.add(pos) = b':' as c_char; pos += 1;
13456                    *s.add(pos) = b':' as c_char; pos += 1;
13457                }
13458                i += 1;
13459                prev_in_run = true;
13460                continue;
13461            }
13462            if i > 0 && !prev_in_run {
13463                *s.add(pos) = b':' as c_char; pos += 1;
13464            }
13465            let (buf, len) = format_hex(words[i] as u64, false);
13466            for k in 0..len { *s.add(pos) = buf[k] as c_char; pos += 1; }
13467            i += 1;
13468            prev_in_run = false;
13469        }
13470        *s.add(pos) = 0;
13471        s
13472    } else {
13473        ERRNO!() = EAFNOSUPPORT;
13474        core::ptr::null_mut()
13475    }
13476}
13477
13478#[no_mangle]
13479pub unsafe extern "C" fn inet_aton(s: *const c_char, dest: *mut c_void) -> c_int {
13480    let a = dest as *mut u8;
13481    let mut p = s as *const u8;
13482    let mut vals = [0u64; 4];
13483    let mut i = 0usize;
13484    while i < 4 {
13485        if *p < b'0' || *p > b'9' { return 0; }
13486        let mut base = 10u32;
13487        if *p == b'0' {
13488            let c = *p.add(1);
13489            if c == b'x' || c == b'X' {
13490                base = 16;
13491                p = p.add(2);
13492                if !((*p >= b'0' && *p <= b'9')
13493                    || (*p >= b'a' && *p <= b'f')
13494                    || (*p >= b'A' && *p <= b'F')) {
13495                    return 0;
13496                }
13497            } else {
13498                base = 8;
13499            }
13500        }
13501        let mut v: u64 = 0;
13502        let start = p;
13503        loop {
13504            let c = *p;
13505            let d = if c >= b'0' && c <= b'9' {
13506                (c - b'0') as u64
13507            } else if base == 16 && c >= b'a' && c <= b'f' {
13508                (c - b'a' + 10) as u64
13509            } else if base == 16 && c >= b'A' && c <= b'F' {
13510                (c - b'A' + 10) as u64
13511            } else {
13512                break;
13513            };
13514            v = v * (base as u64) + d;
13515            p = p.add(1);
13516        }
13517        if p == start { return 0; }
13518        vals[i] = v;
13519        if *p == 0 {
13520            break;
13521        }
13522        if *p != b'.' || i == 3 { return 0; }
13523        p = p.add(1);
13524        i += 1;
13525    }
13526    if i == 4 { return 0; }
13527    if i < 1 { vals[1] = vals[0] & 0xffffff; vals[0] >>= 24; }
13528    if i < 2 { vals[2] = vals[1] & 0xffff; vals[1] >>= 16; }
13529    if i < 3 { vals[3] = vals[2] & 0xff; vals[2] >>= 8; }
13530    for j in 0..4 {
13531        if vals[j] > 255 { return 0; }
13532        *a.add(j) = vals[j] as u8;
13533    }
13534    1
13535}
13536
13537#[no_mangle]
13538pub unsafe extern "C" fn inet_addr(s: *const c_char) -> u32 {
13539    let mut a = [0u8; 4];
13540    if inet_aton(s, a.as_mut_ptr() as *mut c_void) == 1 {
13541        u32::from_ne_bytes(a)
13542    } else {
13543        !0u32 // INADDR_NONE
13544    }
13545}
13546
13547#[no_mangle]
13548pub unsafe extern "C" fn inet_ntoa(addr: u32) -> *mut c_char {
13549    static mut NTOA_BUF: [c_char; 16] = [0; 16];
13550    let b = addr.to_ne_bytes();
13551    let mut pos = 0;
13552    for i in 0..4 {
13553        if i > 0 { NTOA_BUF[pos] = b'.' as c_char; pos += 1; }
13554        let (buf, len) = format_u64(b[i] as u64);
13555        for j in 0..len { NTOA_BUF[pos] = buf[j] as c_char; pos += 1; }
13556    }
13557    NTOA_BUF[pos] = 0;
13558    core::ptr::addr_of_mut!(NTOA_BUF).cast::<c_char>()
13559}
13560
13561// ponytail: inet_network/inet_makeaddr/inet_lnaof/inet_netof - simple implementations
13562#[no_mangle]
13563pub unsafe extern "C" fn inet_network(s: *const c_char) -> u32 {
13564    inet_addr(s).to_be()
13565}
13566
13567#[no_mangle]
13568pub extern "C" fn inet_makeaddr(net: c_int, host: c_int) -> u32 {
13569    let addr: u32;
13570    if (net as u32) < 128 {
13571        addr = (net as u32) << 24;
13572    } else if (net as u32) < 0x10000 {
13573        addr = (net as u32) << 16;
13574    } else if (net as u32) < 0x1000000 {
13575        addr = (net as u32) << 8;
13576    } else {
13577        addr = net as u32;
13578    }
13579    addr | (host as u32 & 0xffffffff)
13580}
13581
13582#[no_mangle]
13583pub extern "C" fn inet_lnaof(addr: u32) -> c_int {
13584    let h = u32::from_be(addr);
13585    if h < 0x80000000 { (h & 0x00ffffff) as c_int }
13586    else if h < 0xc0000000 { (h & 0x0000ffff) as c_int }
13587    else { (h & 0x000000ff) as c_int }
13588}
13589
13590#[no_mangle]
13591pub extern "C" fn inet_netof(addr: u32) -> c_int {
13592    let h = u32::from_be(addr);
13593    if h < 0x80000000 { (h >> 24) as c_int }
13594    else if h < 0xc0000000 { (h >> 16) as c_int }
13595    else { (h >> 8) as c_int }
13596}
13597
13598// ============================================================
13599// stdlib: strtod / strtof / strtold
13600// ============================================================
13601
13602// ponytail: correctly-rounded float parser.
13603// Decimal: collects digits into buffer, uses core::str::FromStr.
13604// Hex: u128 integer arithmetic with round-to-nearest-even.
13605// is_f32: true for strtof (f32::from_str), false for strtod/strtold (f64::from_str).
13606unsafe fn parse_float(s: *const u8, endptr: *mut *mut u8, is_f32: bool) -> f64 {
13607    let mut p = s;
13608    // skip whitespace
13609    while *p == b' ' || *p == b'\t' || *p == b'\n' || *p == b'\r' { p = p.add(1); }
13610    let mut neg = false;
13611    if *p == b'-' { neg = true; p = p.add(1); }
13612    else if *p == b'+' { p = p.add(1); }
13613
13614    // inf
13615    if (*p | 0x20) == b'i'
13616        && (*p.add(1) | 0x20) == b'n'
13617        && (*p.add(2) | 0x20) == b'f'
13618    {
13619        p = p.add(3);
13620        // optionally consume "inity"
13621        if (*p | 0x20) == b'i'
13622            && (*p.add(1) | 0x20) == b't'
13623            && (*p.add(2) | 0x20) == b'y'
13624        {
13625            p = p.add(3);
13626        }
13627        if !endptr.is_null() { *endptr = p as *mut u8; }
13628        return if neg { f64::NEG_INFINITY } else { f64::INFINITY };
13629    }
13630    // nan
13631    if (*p | 0x20) == b'n'
13632        && (*p.add(1) | 0x20) == b'a'
13633        && (*p.add(2) | 0x20) == b'n'
13634    {
13635        p = p.add(3);
13636        // optionally consume (n-char-sequence)
13637        if *p == b'(' {
13638            p = p.add(1);
13639            while *p != 0 && *p != b')' { p = p.add(1); }
13640            if *p == b')' { p = p.add(1); }
13641        }
13642        if !endptr.is_null() { *endptr = p as *mut u8; }
13643        return if neg { -f64::NAN } else { f64::NAN };
13644    }
13645
13646    // hex float
13647    if *p == b'0' && (*p.add(1) == b'x' || *p.add(1) == b'X') {
13648        p = p.add(2);
13649        let mut mant: u128 = 0;
13650        let mut frac_hex: u32 = 0;
13651        let mut found = false;
13652        let mut in_frac = false;
13653
13654        loop {
13655            if let Some(d) = hex_val(*p) {
13656                if mant <= u128::MAX / 16 {
13657                    mant = mant * 16 + d as u128;
13658                }
13659                if in_frac { frac_hex += 1; }
13660                p = p.add(1);
13661                found = true;
13662            } else if *p == b'.' && !in_frac {
13663                in_frac = true;
13664                p = p.add(1);
13665            } else {
13666                break;
13667            }
13668        }
13669
13670        if !found {
13671            if !endptr.is_null() { *endptr = s as *mut u8; }
13672            return 0.0;
13673        }
13674
13675        // binary exponent p[+-]N
13676        let mut bin_exp: i32 = 0;
13677        if *p == b'p' || *p == b'P' {
13678            p = p.add(1);
13679            let mut exp_neg = false;
13680            if *p == b'-' { exp_neg = true; p = p.add(1); }
13681            else if *p == b'+' { p = p.add(1); }
13682            while *p >= b'0' && *p <= b'9' {
13683                if bin_exp < 100000 {
13684                    bin_exp = bin_exp * 10 + (*p - b'0') as i32;
13685                }
13686                p = p.add(1);
13687            }
13688            if exp_neg { bin_exp = -bin_exp; }
13689        }
13690
13691        if !endptr.is_null() { *endptr = p as *mut u8; }
13692        if mant == 0 { return if neg { -0.0 } else { 0.0 }; }
13693
13694        let total_exp = bin_exp as i64 - 4 * frac_hex as i64;
13695        return hex_mant_to_f64(mant, total_exp, neg);
13696    }
13697
13698    // decimal: collect into buffer, use from_str for correct rounding
13699    let mut buf = [0u8; 65536];
13700    let mut n = 0usize;
13701    let mut found_digit = false;
13702
13703    while *p >= b'0' && *p <= b'9' {
13704        if n < 65535 { buf[n] = *p; n += 1; }
13705        p = p.add(1);
13706        found_digit = true;
13707    }
13708    if *p == b'.' {
13709        if n < 65535 { buf[n] = b'.'; n += 1; }
13710        p = p.add(1);
13711        while *p >= b'0' && *p <= b'9' {
13712            if n < 65535 { buf[n] = *p; n += 1; }
13713            p = p.add(1);
13714            found_digit = true;
13715        }
13716    }
13717    if !found_digit {
13718        if !endptr.is_null() { *endptr = s as *mut u8; }
13719        return 0.0;
13720    }
13721    if *p == b'e' || *p == b'E' {
13722        if n < 65535 { buf[n] = b'e'; n += 1; }
13723        p = p.add(1);
13724        if *p == b'-' || *p == b'+' {
13725            if n < 65535 { buf[n] = *p; n += 1; }
13726            p = p.add(1);
13727        }
13728        while *p >= b'0' && *p <= b'9' {
13729            if n < 65535 { buf[n] = *p; n += 1; }
13730            p = p.add(1);
13731        }
13732    }
13733
13734    if !endptr.is_null() { *endptr = p as *mut u8; }
13735    let s_str = core::str::from_utf8_unchecked(
13736        core::slice::from_raw_parts(buf.as_ptr(), n),
13737    );
13738    if is_f32 {
13739        match <f32 as core::str::FromStr>::from_str(s_str) {
13740            Ok(v) => { let r = v as f64; if neg { -r } else { r } }
13741            Err(_) => { if !endptr.is_null() { *endptr = s as *mut u8; } 0.0 }
13742        }
13743    } else {
13744        match <f64 as core::str::FromStr>::from_str(s_str) {
13745            Ok(v) => if neg { -v } else { v },
13746            Err(_) => { if !endptr.is_null() { *endptr = s as *mut u8; } 0.0 }
13747        }
13748    }
13749}
13750
13751// Convert hex mantissa integer to f64 with correct rounding.
13752// mant: integer from parsed hex digits. total_exp: bin_exp - 4 * frac_hex_digits.
13753// Value = mant * 2^total_exp.
13754fn hex_mant_to_f64(mant: u128, total_exp: i64, neg: bool) -> f64 {
13755    if mant == 0 { return if neg { -0.0 } else { 0.0 }; }
13756
13757    let msb = (127 - mant.leading_zeros()) as i64;
13758    let unbiased_exp = msb + total_exp;
13759    let biased_exp = unbiased_exp + 1023;
13760
13761    // Overflow → infinity
13762    if biased_exp >= 2047 {
13763        return if neg { f64::NEG_INFINITY } else { f64::INFINITY };
13764    }
13765
13766    // Subnormal or underflow
13767    if biased_exp <= 0 {
13768        // subnormal: biased_exp=0, effective exponent=-1022
13769        // mantissa = round(mant * 2^(total_exp + 1074))
13770        let shift = total_exp + 1074;
13771        if shift < 0 {
13772            return if neg { -0.0 } else { 0.0 };
13773        }
13774        if shift > 128 {
13775            return if neg { f64::NEG_INFINITY } else { f64::INFINITY };
13776        }
13777        let shifted = mant << (shift as u32);
13778        let mantissa52 = (shifted as u64) & 0x000FFFFFFFFFFFFF;
13779        let guard = ((shifted >> 52) & 1) != 0;
13780        let sticky = (shifted >> 53) != 0;
13781        let mut result = mantissa52;
13782        if guard && (sticky || (result & 1) != 0) {
13783            result += 1;
13784            if result > 0x000FFFFFFFFFFFFF {
13785                // carry → smallest normal
13786                return if neg { -f64::from_bits(1u64 << 52) } else { f64::from_bits(1u64 << 52) };
13787            }
13788        }
13789        return if neg { -f64::from_bits(result) } else { f64::from_bits(result) };
13790    }
13791
13792    // Normal: biased_exp in [1, 2046]
13793    let shift = msb - 52;
13794
13795    if shift <= 0 {
13796        // fewer than 53 bits, no rounding needed
13797        let mantissa = ((mant as u64) & ((1u64 << msb as u32) - 1)) << ((-shift) as u32);
13798        let bits = ((biased_exp as u64) << 52) | mantissa;
13799        return if neg { -f64::from_bits(bits) } else { f64::from_bits(bits) };
13800    }
13801
13802    // shift > 0: round to nearest even
13803    let mantissa = ((mant >> shift as u32) as u64) & 0x000FFFFFFFFFFFFF;
13804    let guard = ((mant >> (shift as u32 - 1)) & 1) != 0;
13805    let sticky = if shift > 1 {
13806        (mant & ((1u128 << (shift as u32 - 1)) - 1)) != 0
13807    } else { false };
13808
13809    let mut result_mant = mantissa;
13810    let mut result_exp = biased_exp;
13811    if guard && (sticky || (result_mant & 1) != 0) {
13812        result_mant += 1;
13813        if result_mant > 0x000FFFFFFFFFFFFF {
13814            result_mant = 0;
13815            result_exp += 1;
13816            if result_exp >= 2047 {
13817                return if neg { f64::NEG_INFINITY } else { f64::INFINITY };
13818            }
13819        }
13820    }
13821
13822    let bits = ((result_exp as u64) << 52) | result_mant;
13823    if neg { -f64::from_bits(bits) } else { f64::from_bits(bits) }
13824}
13825
13826unsafe fn hex_val(c: u8) -> Option<u8> {
13827    if c >= b'0' && c <= b'9' { Some(c - b'0') }
13828    else if c >= b'a' && c <= b'f' { Some(c - b'a' + 10) }
13829    else if c >= b'A' && c <= b'F' { Some(c - b'A' + 10) }
13830    else { None }
13831}
13832
13833#[no_mangle]
13834pub unsafe extern "C" fn strtod(s: *const c_char, endptr: *mut *mut c_char) -> f64 {
13835    let mut end: *mut u8 = s as *mut u8;
13836    let r = parse_float(s as *const u8, &mut end, false);
13837    if !endptr.is_null() { *endptr = end as *mut c_char; }
13838    r
13839}
13840
13841#[no_mangle]
13842pub unsafe extern "C" fn strtof(s: *const c_char, endptr: *mut *mut c_char) -> f32 {
13843    let mut end: *mut u8 = s as *mut u8;
13844    let r = parse_float(s as *const u8, &mut end, true);
13845    if !endptr.is_null() { *endptr = end as *mut c_char; }
13846    r as f32
13847}
13848
13849#[no_mangle]
13850#[cfg(target_arch = "x86_64")]
13851pub unsafe extern "C" fn strtold(s: *const c_char, endptr: *mut *mut c_char) -> f64 {
13852    strtod(s, endptr)
13853}
13854
13855#[no_mangle]
13856#[cfg(target_arch = "aarch64")]
13857pub unsafe extern "C" fn strtold(s: *const c_char, endptr: *mut *mut c_char) -> f128 {
13858    strtod(s, endptr) as f128
13859}
13860
13861#[no_mangle]
13862#[cfg(target_arch = "riscv64")]
13863pub unsafe extern "C" fn strtold(s: *const c_char, endptr: *mut *mut c_char) -> f128 {
13864    strtod(s, endptr) as f128
13865}
13866
13867#[no_mangle]
13868pub unsafe extern "C" fn atof(s: *const c_char) -> f64 {
13869    strtod(s, core::ptr::null_mut())
13870}
13871
13872// ============================================================
13873// stdio: open_memstream / fmemopen
13874// ============================================================
13875
13876#[repr(C)]
13877struct MsCookie {
13878    bufp: *mut *mut c_char,
13879    sizep: *mut usize,
13880    pos: usize,
13881    len: usize,
13882    buf: *mut u8,
13883    space: usize,
13884}
13885
13886#[repr(C)]
13887struct MfCookie {
13888    pos: usize,
13889    len: usize,
13890    size: usize,
13891    buf: *mut u8,
13892    mode: c_char,
13893}
13894
13895unsafe extern "C" fn ms_seek(f: *mut FILE, off: i64, whence: c_int) -> i64 {
13896    let c = (*f).cookie as *mut MsCookie;
13897    if whence < 0 || whence > 2 { ERRNO!() = EINVAL; return -1; }
13898    let base: i64 = match whence {
13899        0 => 0,
13900        1 => (*c).pos as i64,
13901        2 => (*c).len as i64,
13902        _ => { ERRNO!() = EINVAL; return -1; }
13903    };
13904    if off < -base || off > isize::MAX as i64 - base { ERRNO!() = EINVAL; return -1; }
13905    (*c).pos = (base + off) as usize;
13906    (*c).pos as i64
13907}
13908
13909unsafe extern "C" fn ms_write(f: *mut FILE, buf: *const u8, len: usize) -> usize {
13910    let c = (*f).cookie as *mut MsCookie;
13911    let buffered = (*f).wpos as usize - (*f).wbase as usize;
13912    if buffered > 0 {
13913        (*f).wpos = (*f).wbase;
13914        if ms_write(f, (*f).wbase, buffered) < buffered { return 0; }
13915    }
13916    if len == 0 { return 0; }
13917    if (*c).pos + len >= (*c).space {
13918        let new_space = 2 * (*c).space + 1 | (*c).pos + len + 1;
13919        let newbuf = realloc((*c).buf as *mut c_void, new_space) as *mut u8;
13920        if newbuf.is_null() { return 0; }
13921        *(*c).bufp = newbuf as *mut c_char;
13922        core::ptr::write_bytes(newbuf.add((*c).space), 0, new_space - (*c).space);
13923        (*c).buf = newbuf;
13924        (*c).space = new_space;
13925    }
13926    core::ptr::copy_nonoverlapping(buf, (*c).buf.add((*c).pos), len);
13927    (*c).pos += len;
13928    if (*c).pos >= (*c).len { (*c).len = (*c).pos; }
13929    *(*c).sizep = (*c).pos;
13930    len
13931}
13932
13933unsafe extern "C" fn ms_close(_f: *mut FILE) -> c_int {
13934    0
13935}
13936
13937#[no_mangle]
13938pub unsafe extern "C" fn open_memstream(bufp: *mut *mut c_char, sizep: *mut usize) -> *mut FILE {
13939    if bufp.is_null() || sizep.is_null() { ERRNO!() = EINVAL; return core::ptr::null_mut(); }
13940    let f = calloc(1, core::mem::size_of::<FILE>() + UNGET + BUFSIZ) as *mut FILE;
13941    if f.is_null() { ERRNO!() = ENOMEM; return core::ptr::null_mut(); }
13942    let buf_area = buf_ptr(f);
13943    init_file(f, -1, b"w\0".as_ptr() as *const c_char, Some(ms_close), buf_area, BUFSIZ);
13944    (*f).flags = F_NORD | F_SVB;
13945    (*f).lbf = -1;
13946    let c = calloc(1, core::mem::size_of::<MsCookie>()) as *mut MsCookie;
13947    if c.is_null() { free(f as *mut c_void); ERRNO!() = ENOMEM; return core::ptr::null_mut(); }
13948    (*c).bufp = bufp;
13949    (*c).sizep = sizep;
13950    (*c).pos = 0;
13951    (*c).len = 0;
13952    (*c).buf = core::ptr::null_mut();
13953    (*c).space = 0;
13954    (*f).cookie = c as *mut c_void;
13955    (*f).write_fn = Some(ms_write);
13956    (*f).seek_fn = Some(ms_seek);
13957    *bufp = core::ptr::null_mut();
13958    *sizep = 0;
13959    f
13960}
13961
13962unsafe extern "C" fn mseek(f: *mut FILE, off: i64, whence: c_int) -> i64 {
13963    let c = (*f).cookie as *mut MfCookie;
13964    if whence < 0 || whence > 2 { ERRNO!() = EINVAL; return -1; }
13965    let base: i64 = match whence {
13966        0 => 0,
13967        1 => (*c).pos as i64,
13968        2 => (*c).len as i64,
13969        _ => { ERRNO!() = EINVAL; return -1; }
13970    };
13971    if off < -base || off > (*c).size as i64 - base { ERRNO!() = EINVAL; return -1; }
13972    (*c).pos = (base + off) as usize;
13973    (*c).pos as i64
13974}
13975
13976unsafe extern "C" fn mread(f: *mut FILE, buf: *mut u8, len: usize) -> usize {
13977    let c = (*f).cookie as *mut MfCookie;
13978    let mut rem = if (*c).pos <= (*c).len { (*c).len - (*c).pos } else { 0 };
13979    let mut l = len;
13980    if l > rem {
13981        l = rem;
13982        (*f).flags |= F_EOF;
13983    }
13984    core::ptr::copy_nonoverlapping((*c).buf.add((*c).pos), buf, l);
13985    (*c).pos += l;
13986    rem -= l;
13987    if rem > (*f).buf_size { rem = (*f).buf_size; }
13988    (*f).rpos = (*f).buf;
13989    (*f).rend = (*f).buf.add(rem);
13990    core::ptr::copy_nonoverlapping((*c).buf.add((*c).pos), (*f).rpos, rem);
13991    (*c).pos += rem;
13992    l
13993}
13994
13995unsafe extern "C" fn mwrite(f: *mut FILE, buf: *const u8, len: usize) -> usize {
13996    let c = (*f).cookie as *mut MfCookie;
13997    let buffered = (*f).wpos as usize - (*f).wbase as usize;
13998    if buffered > 0 {
13999        (*f).wpos = (*f).wbase;
14000        if mwrite(f, (*f).wbase, buffered) < buffered { return 0; }
14001    }
14002    if len == 0 { return 0; }
14003    if (*c).mode == b'a' as c_char { (*c).pos = (*c).len; }
14004    let rem = (*c).size - (*c).pos;
14005    let mut l = len;
14006    if l > rem { l = rem; }
14007    core::ptr::copy_nonoverlapping(buf, (*c).buf.add((*c).pos), l);
14008    (*c).pos += l;
14009    if (*c).pos > (*c).len {
14010        (*c).len = (*c).pos;
14011        if (*c).len < (*c).size {
14012            *(*c).buf.add((*c).len) = 0;
14013        } else if ((*f).flags & F_NORD) != 0 && (*c).size > 0 {
14014            *(*c).buf.add((*c).size - 1) = 0;
14015        }
14016    }
14017    l
14018}
14019
14020unsafe extern "C" fn mclose(_f: *mut FILE) -> c_int {
14021    0
14022}
14023
14024#[no_mangle]
14025pub unsafe extern "C" fn fmemopen(buf: *mut c_void, size: usize, mode: *const c_char) -> *mut FILE {
14026    if mode.is_null() { ERRNO!() = EINVAL; return core::ptr::null_mut(); }
14027    let m = *mode;
14028    if m != b'r' as c_char && m != b'w' as c_char && m != b'a' as c_char {
14029        ERRNO!() = EINVAL; return core::ptr::null_mut();
14030    }
14031    let has_plus = !strchr(mode as *const u8, b'+' as c_int).is_null();
14032    let need_alloc = buf.is_null();
14033    let alloc_size = core::mem::size_of::<FILE>() + UNGET + BUFSIZ + if need_alloc { size } else { 0 };
14034    let f = calloc(1, alloc_size) as *mut FILE;
14035    if f.is_null() { ERRNO!() = ENOMEM; return core::ptr::null_mut(); }
14036    let buf_area = buf_ptr(f);
14037    init_file(f, -1, mode, Some(mclose), buf_area, BUFSIZ);
14038    (*f).flags |= F_SVB;
14039    let actual_buf: *mut u8 = if need_alloc {
14040        (f as *mut u8).add(alloc_size - size)
14041    } else {
14042        buf as *mut u8
14043    };
14044    if need_alloc { core::ptr::write_bytes(actual_buf, 0, size); }
14045    let c = calloc(1, core::mem::size_of::<MfCookie>()) as *mut MfCookie;
14046    if c.is_null() { free(f as *mut c_void); ERRNO!() = ENOMEM; return core::ptr::null_mut(); }
14047    (*c).buf = actual_buf;
14048    (*c).size = size;
14049    (*c).mode = m;
14050    (*f).cookie = c as *mut c_void;
14051    (*f).read_fn = Some(mread);
14052    (*f).write_fn = Some(mwrite);
14053    (*f).seek_fn = Some(mseek);
14054    if !has_plus { (*f).flags = if m == b'r' as c_char { F_NOWR } else { F_NORD }; }
14055    if m == b'r' as c_char {
14056        (*c).len = size;
14057    } else if m == b'a' as c_char {
14058        (*c).len = strnlen(actual_buf, size);
14059        (*c).pos = (*c).len;
14060    } else if has_plus {
14061        *actual_buf = 0;
14062    }
14063    f
14064}
14065
14066// ============================================================
14067// Stat (for ftok)
14068// ============================================================
14069
14070#[repr(C)]
14071struct kernel_stat64 {
14072    st_dev: u64,
14073    st_ino: u64,
14074    st_nlink: u64,
14075    st_mode: u32,
14076    st_uid: u32,
14077    st_gid: u32,
14078    __pad0: c_int,
14079    st_rdev: u64,
14080    st_size: i64,
14081    st_blksize: i64,
14082    st_blocks: i64,
14083    st_atime: i64,
14084    st_atime_nsec: i64,
14085    st_mtime: i64,
14086    st_mtime_nsec: i64,
14087    st_ctime: i64,
14088    st_ctime_nsec: i64,
14089    __unused: [i64; 3],
14090}
14091
14092// sys_stat = 4 on x86_64
14093#[inline]
14094unsafe fn sys_stat(path: *const u8, statbuf: *mut kernel_stat64) -> i64 {
14095    <Arch as Syscalls>::syscall4(SYS_NEWFSTATAT, AT_FDCWD as i64, path as i64, statbuf as i64, 0)
14096}
14097
14098// ============================================================
14099// Search: hcreate/hdestroy/hsearch (hash table)
14100// ============================================================
14101
14102const HSEARCH_MINSIZE: usize = 8;
14103
14104#[repr(C)]
14105#[derive(Copy, Clone)]
14106pub struct HSearchEntry {
14107    key: *mut c_char,
14108    data: *mut c_void,
14109}
14110
14111#[repr(C)]
14112struct HTab {
14113    entries: *mut HSearchEntry,
14114    mask: usize,
14115    used: usize,
14116}
14117
14118#[repr(C)]
14119pub struct HSearchData {
14120    tab: *mut HTab,
14121    __unused1: c_uint,
14122    __unused2: c_uint,
14123}
14124
14125// ponytail: single static hash table for non-_r variants
14126static mut HTAB_DATA: HSearchData = HSearchData {
14127    tab: core::ptr::null_mut(),
14128    __unused1: 0,
14129    __unused2: 0,
14130};
14131
14132unsafe fn keyhash(k: *const c_char) -> usize {
14133    let mut h: usize = 0;
14134    let mut p = k;
14135    while *p != 0 {
14136        h = h.wrapping_mul(31).wrapping_add(*p as usize);
14137        p = p.add(1);
14138    }
14139    h
14140}
14141
14142unsafe fn htab_lookup(key: *const c_char, hash: usize, htab: *mut HSearchData) -> *mut HSearchEntry {
14143    let tab = (*htab).tab;
14144    let mask = (*tab).mask;
14145    let entries = (*tab).entries;
14146    let mut i = hash;
14147    let mut j: usize = 1;
14148    loop {
14149        let e = entries.add(i & mask);
14150        if (*e).key.is_null() || strcmp((*e).key as *const u8, key as *const u8) == 0 {
14151            return e;
14152        }
14153        i = i.wrapping_add(j);
14154        j += 1;
14155    }
14156}
14157
14158unsafe fn htab_resize(nel: usize, htab: *mut HSearchData) -> c_int {
14159    let tab = (*htab).tab;
14160    let old_entries = (*tab).entries;
14161    let old_size = (*tab).mask + 1;
14162
14163    let mut newsize: usize = HSEARCH_MINSIZE;
14164    while newsize < nel {
14165        let Some(next) = newsize.checked_mul(2) else {
14166            ERRNO!() = ENOMEM;
14167            return 0;
14168        };
14169        newsize = next;
14170    }
14171
14172    let new_entries = calloc(newsize, core::mem::size_of::<HSearchEntry>()) as *mut HSearchEntry;
14173    if new_entries.is_null() {
14174        ERRNO!() = ENOMEM;
14175        return 0;
14176    }
14177    (*tab).entries = new_entries;
14178    (*tab).mask = newsize - 1;
14179
14180    if !old_entries.is_null() {
14181        for idx in 0..old_size {
14182            let old_e = old_entries.add(idx);
14183            if !(*old_e).key.is_null() {
14184                let h = keyhash((*old_e).key);
14185                let mut i = h;
14186                let mut j: usize = 1;
14187                loop {
14188                    let e = new_entries.add(i & (*tab).mask);
14189                    if (*e).key.is_null() {
14190                        *e = *old_e;
14191                        break;
14192                    }
14193                    i = i.wrapping_add(j);
14194                    j += 1;
14195                }
14196            }
14197        }
14198        free(old_entries as *mut c_void);
14199    }
14200    1
14201}
14202
14203unsafe fn hcreate_r_impl(nel: usize, htab: *mut HSearchData) -> c_int {
14204    if nel == 0 || nel > (1usize << 30) {
14205        ERRNO!() = ENOMEM;
14206        return 0;
14207    }
14208    (*htab).tab = calloc(1, core::mem::size_of::<HTab>()) as *mut HTab;
14209    if (*htab).tab.is_null() {
14210        ERRNO!() = ENOMEM;
14211        return 0;
14212    }
14213    if htab_resize(nel, htab) == 0 {
14214        free((*htab).tab as *mut c_void);
14215        (*htab).tab = core::ptr::null_mut();
14216        return 0;
14217    }
14218    1
14219}
14220
14221unsafe fn hdestroy_r_impl(htab: *mut HSearchData) {
14222    if !(*htab).tab.is_null() {
14223        if !(*(*htab).tab).entries.is_null() {
14224            free((*(*htab).tab).entries as *mut c_void);
14225        }
14226        free((*htab).tab as *mut c_void);
14227        (*htab).tab = core::ptr::null_mut();
14228    }
14229}
14230
14231unsafe fn hsearch_r_impl(item: HSearchEntry, action: c_int, retval: *mut *mut HSearchEntry, htab: *mut HSearchData) -> c_int {
14232    let hash = keyhash(item.key);
14233    let e = htab_lookup(item.key, hash, htab);
14234
14235    if !(*e).key.is_null() {
14236        *retval = e;
14237        return 1;
14238    }
14239    if action == 0 { // FIND
14240        *retval = core::ptr::null_mut();
14241        return 0;
14242    }
14243    // ENTER
14244    let item_key = item.key;
14245    *e = item;
14246    (*(*htab).tab).used += 1;
14247    if (*(*htab).tab).used > (*(*htab).tab).mask - (*(*htab).tab).mask / 4 {
14248        if htab_resize(2 * (*(*htab).tab).used, htab) == 0 {
14249            (*(*htab).tab).used -= 1;
14250            (*e).key = core::ptr::null_mut();
14251            *retval = core::ptr::null_mut();
14252            return 0;
14253        }
14254        let e2 = htab_lookup(item_key, hash, htab);
14255        *retval = e2;
14256        return 1;
14257    }
14258    *retval = e;
14259    1
14260}
14261
14262#[no_mangle]
14263pub unsafe extern "C" fn hcreate(nel: usize) -> c_int {
14264    hcreate_r_impl(nel, core::ptr::addr_of_mut!(HTAB_DATA))
14265}
14266
14267#[no_mangle]
14268pub unsafe extern "C" fn hdestroy() {
14269    hdestroy_r_impl(core::ptr::addr_of_mut!(HTAB_DATA));
14270}
14271
14272#[no_mangle]
14273pub unsafe extern "C" fn hsearch(item: HSearchEntry, action: c_int) -> *mut HSearchEntry {
14274    let mut e: *mut HSearchEntry = core::ptr::null_mut();
14275    hsearch_r_impl(item, action, &mut e, core::ptr::addr_of_mut!(HTAB_DATA));
14276    e
14277}
14278
14279#[no_mangle]
14280pub unsafe extern "C" fn hcreate_r(nel: usize, htab: *mut HSearchData) -> c_int {
14281    hcreate_r_impl(nel, htab)
14282}
14283
14284#[no_mangle]
14285pub unsafe extern "C" fn hdestroy_r(htab: *mut HSearchData) {
14286    hdestroy_r_impl(htab);
14287}
14288
14289#[no_mangle]
14290pub unsafe extern "C" fn hsearch_r(item: HSearchEntry, action: c_int, retval: *mut *mut HSearchEntry, htab: *mut HSearchData) -> c_int {
14291    hsearch_r_impl(item, action, retval, htab)
14292}
14293
14294// ============================================================
14295// Search: insque / remque
14296// ============================================================
14297
14298#[repr(C)]
14299struct QueNode {
14300    next: *mut QueNode,
14301    prev: *mut QueNode,
14302}
14303
14304#[no_mangle]
14305pub unsafe extern "C" fn insque(element: *mut c_void, pred: *mut c_void) {
14306    let e = element as *mut QueNode;
14307    let p = pred as *mut QueNode;
14308    if p.is_null() {
14309        (*e).next = core::ptr::null_mut();
14310        (*e).prev = core::ptr::null_mut();
14311        return;
14312    }
14313    (*e).next = (*p).next;
14314    (*e).prev = p;
14315    (*p).next = e;
14316    if !(*e).next.is_null() {
14317        (*(*e).next).prev = e;
14318    }
14319}
14320
14321#[no_mangle]
14322pub unsafe extern "C" fn remque(element: *mut c_void) {
14323    let e = element as *mut QueNode;
14324    if !(*e).next.is_null() {
14325        (*(*e).next).prev = (*e).prev;
14326    }
14327    if !(*e).prev.is_null() {
14328        (*(*e).prev).next = (*e).next;
14329    }
14330}
14331
14332// ============================================================
14333// Search: lsearch / lfind
14334// ============================================================
14335
14336#[no_mangle]
14337pub unsafe extern "C" fn lsearch(
14338    key: *const c_void,
14339    base: *mut c_void,
14340    nelp: *mut usize,
14341    width: usize,
14342    compar: Option<unsafe extern "C" fn(*const c_void, *const c_void) -> c_int>,
14343) -> *mut c_void {
14344    let n = *nelp;
14345    let p = base as *mut u8;
14346    for i in 0..n {
14347        let elem = p.add(i * width);
14348        if compar.unwrap()(key, elem as *const c_void) == 0 {
14349            return elem as *mut c_void;
14350        }
14351    }
14352    let dest = p.add(n * width);
14353    memcpy(dest as *mut c_void, key as *const c_void, width);
14354    *nelp = n + 1;
14355    dest as *mut c_void
14356}
14357
14358#[no_mangle]
14359pub unsafe extern "C" fn lfind(
14360    key: *const c_void,
14361    base: *const c_void,
14362    nelp: *mut usize,
14363    width: usize,
14364    compar: Option<unsafe extern "C" fn(*const c_void, *const c_void) -> c_int>,
14365) -> *mut c_void {
14366    let n = *nelp;
14367    let p = base as *const u8;
14368    for i in 0..n {
14369        let elem = p.add(i * width);
14370        if compar.unwrap()(key, elem as *const c_void) == 0 {
14371            return elem as *mut c_void;
14372        }
14373    }
14374    core::ptr::null_mut()
14375}
14376
14377// ============================================================
14378// Search: tsearch / tfind / tdelete / twalk / tdestroy (BST, no balancing)
14379// ============================================================
14380
14381// ponytail: simple BST, no balancing; correct for tests, O(n) worst case
14382#[repr(C)]
14383pub struct TreeNode {
14384    key: *const c_void,
14385    left: *mut TreeNode,
14386    right: *mut TreeNode,
14387}
14388
14389type ComparFn = Option<unsafe extern "C" fn(*const c_void, *const c_void) -> c_int>;
14390
14391unsafe fn bst_new(key: *const c_void) -> *mut TreeNode {
14392    let n = calloc(1, core::mem::size_of::<TreeNode>()) as *mut TreeNode;
14393    if !n.is_null() {
14394        (*n).key = key;
14395        (*n).left = core::ptr::null_mut();
14396        (*n).right = core::ptr::null_mut();
14397    }
14398    n
14399}
14400
14401#[no_mangle]
14402pub unsafe extern "C" fn tsearch(
14403    key: *const c_void,
14404    rootp: *mut *mut TreeNode,
14405    compar: ComparFn,
14406) -> *mut c_void {
14407    if rootp.is_null() {
14408        return core::ptr::null_mut();
14409    }
14410    let mut pp = rootp;
14411    loop {
14412        let n = *pp;
14413        if n.is_null() {
14414            let r = bst_new(key);
14415            if r.is_null() {
14416                return core::ptr::null_mut();
14417            }
14418            *pp = r;
14419            return r as *mut c_void;
14420        }
14421        let c = compar.unwrap()(key, (*n).key);
14422        if c == 0 {
14423            return n as *mut c_void;
14424        }
14425        if c < 0 {
14426            pp = &mut (*n).left;
14427        } else {
14428            pp = &mut (*n).right;
14429        }
14430    }
14431}
14432
14433#[no_mangle]
14434pub unsafe extern "C" fn tfind(
14435    key: *const c_void,
14436    rootp: *const *mut TreeNode,
14437    compar: ComparFn,
14438) -> *mut c_void {
14439    if rootp.is_null() {
14440        return core::ptr::null_mut();
14441    }
14442    let mut n = *rootp;
14443    loop {
14444        if n.is_null() {
14445            return core::ptr::null_mut();
14446        }
14447        let c = compar.unwrap()(key, (*n).key);
14448        if c == 0 {
14449            return n as *mut c_void;
14450        }
14451        if c < 0 {
14452            n = (*n).left;
14453        } else {
14454            n = (*n).right;
14455        }
14456    }
14457}
14458
14459#[no_mangle]
14460pub unsafe extern "C" fn tdelete(
14461    key: *const c_void,
14462    rootp: *mut *mut TreeNode,
14463    compar: ComparFn,
14464) -> *mut c_void {
14465    if rootp.is_null() {
14466        return core::ptr::null_mut();
14467    }
14468    let mut pp = rootp;
14469    loop {
14470        let n = *pp;
14471        if n.is_null() {
14472            return core::ptr::null_mut();
14473        }
14474        let c = compar.unwrap()(key, (*n).key);
14475        if c == 0 {
14476            break;
14477        }
14478        if c < 0 {
14479            pp = &mut (*n).left;
14480        } else {
14481            pp = &mut (*n).right;
14482        }
14483    }
14484    let n = *pp;
14485    // parent is *rootp if we went to root, otherwise the node above
14486    let parent = *rootp;
14487    if !(*n).left.is_null() {
14488        // replace with in-order predecessor
14489        let mut q = &mut (*n).left;
14490        while !(*(*q)).right.is_null() {
14491            q = &mut (*(*q)).right;
14492        }
14493        let pred = *q;
14494        // swap keys
14495        (*n).key = (*pred).key;
14496        // remove predecessor
14497        *q = (*pred).left;
14498        free(pred as *mut c_void);
14499    } else {
14500        // replace with right child (may be null)
14501        *pp = (*n).right;
14502        free(n as *mut c_void);
14503    }
14504    parent as *mut c_void
14505}
14506
14507#[no_mangle]
14508pub unsafe extern "C" fn twalk(
14509    root: *const TreeNode,
14510    action: Option<unsafe extern "C" fn(*const c_void, c_int, c_int)>,
14511) {
14512    unsafe fn walk(
14513        node: *const TreeNode,
14514        action: Option<unsafe extern "C" fn(*const c_void, c_int, c_int)>,
14515        depth: c_int,
14516    ) {
14517        if node.is_null() {
14518            return;
14519        }
14520        let is_leaf = (*node).left.is_null() && (*node).right.is_null();
14521        if is_leaf {
14522            action.unwrap()(node as *const c_void, 3, depth); // leaf
14523        } else {
14524            action.unwrap()(node as *const c_void, 0, depth); // preorder
14525            walk((*node).left, action, depth + 1);
14526            action.unwrap()(node as *const c_void, 1, depth); // postorder (left done)
14527            walk((*node).right, action, depth + 1);
14528            action.unwrap()(node as *const c_void, 2, depth); // endorder
14529        }
14530    }
14531    walk(root, action, 0);
14532}
14533
14534#[no_mangle]
14535pub unsafe extern "C" fn tdestroy(root: *mut TreeNode, freekey: Option<unsafe extern "C" fn(*mut c_void)>) {
14536    if root.is_null() {
14537        return;
14538    }
14539    tdestroy((*root).left, freekey);
14540    tdestroy((*root).right, freekey);
14541    if let Some(f) = freekey {
14542        f((*root).key as *mut c_void);
14543    }
14544    free(root as *mut c_void);
14545}
14546
14547// ============================================================
14548// fnmatch: full POSIX pattern matching
14549// ============================================================
14550
14551const FNM_PATHNAME: c_int = 0x1;
14552const FNM_NOESCAPE: c_int = 0x2;
14553const FNM_PERIOD: c_int = 0x4;
14554const FNM_LEADING_DIR: c_int = 0x8;
14555const FNM_CASEFOLD: c_int = 0x10;
14556const FNM_NOMATCH: c_int = 1;
14557
14558const FNM_END: c_int = 0;
14559const FNM_STAR: c_int = -5;
14560const FNM_QUESTION: c_int = -4;
14561const FNM_BRACKET: c_int = -3;
14562const FNM_UNMATCHABLE: c_int = -2;
14563
14564fn ascii_tolower(c: u8) -> u8 {
14565    if c >= b'A' && c <= b'Z' { c + 32 } else { c }
14566}
14567
14568fn ascii_toupper(c: u8) -> u8 {
14569    if c >= b'a' && c <= b'z' { c - 32 } else { c }
14570}
14571
14572fn ascii_casefold(k: u8) -> u8 {
14573    let upper = ascii_toupper(k);
14574    if upper != k { upper } else { ascii_tolower(k) }
14575}
14576
14577/// Get next byte from pattern, classifying it.
14578/// Returns (token_type, bytes_consumed).
14579unsafe fn pat_next(pat: *const u8, m: usize, flags: c_int) -> (c_int, usize) {
14580    if m == 0 || *pat == 0 {
14581        return (FNM_END, 0);
14582    }
14583    let mut p = pat;
14584    let mut step = 1usize;
14585    let mut esc = false;
14586    if *p == b'\\' && !(flags & FNM_NOESCAPE != 0) && *p.add(1) != 0 {
14587        p = p.add(1);
14588        step = 2;
14589        esc = true;
14590    }
14591    if *p == b'[' && !esc {
14592        // scan for matching ]
14593        let mut k = 1usize;
14594        if k < m && (*pat.add(k) == b'^' || *pat.add(k) == b'!') { k += 1; }
14595        if k < m && *pat.add(k) == b']' { k += 1; }
14596        while k < m && *pat.add(k) != 0 && *pat.add(k) != b']' {
14597            if k + 1 < m && *pat.add(k) == b'[' &&
14598               (*pat.add(k+1) == b':' || *pat.add(k+1) == b'.' || *pat.add(k+1) == b'=') {
14599                let z = *pat.add(k+1);
14600                k += 2;
14601                if k < m { k += 1; }
14602                while k < m && *pat.add(k) != 0 && (*pat.add(k-1) != z || *pat.add(k) != b']') { k += 1; }
14603                if k == m || *pat.add(k) == 0 { break; }
14604            }
14605            k += 1;
14606        }
14607        if k == m || *pat.add(k) == 0 {
14608            // unmatched [ is literal
14609            return (b'[' as c_int, 1);
14610        }
14611        return (FNM_BRACKET, k + 1);
14612    }
14613    if *p == b'*' && !esc {
14614        return (FNM_STAR, 1);
14615    }
14616    if *p == b'?' && !esc {
14617        return (FNM_QUESTION, 1);
14618    }
14619    (*p as c_int, step)
14620}
14621
14622unsafe fn match_class(name: &[u8], k: u8, kfold: u8) -> bool {
14623    let eq = |c: u8| k == c || kfold == c;
14624    let in_range = |lo: u8, hi: u8| (k >= lo && k <= hi) || (kfold >= lo && kfold <= hi);
14625    match name {
14626        b"alnum" => in_range(b'0', b'9') || in_range(b'A', b'Z') || in_range(b'a', b'z'),
14627        b"alpha" => in_range(b'A', b'Z') || in_range(b'a', b'z'),
14628        b"blank" => eq(b' ') || eq(b'\t'),
14629        b"cntrl" => k < 0x20 || k == 0x7f,
14630        b"digit" => in_range(b'0', b'9'),
14631        b"graph" => k >= 0x21 && k <= 0x7e,
14632        b"lower" => in_range(b'a', b'z'),
14633        b"print" => k >= 0x20 && k <= 0x7e,
14634        b"punct" =>
14635            (k >= 0x21 && k <= 0x2f) ||
14636            (k >= 0x3a && k <= 0x40) ||
14637            (k >= 0x5b && k <= 0x60) ||
14638            (k >= 0x7b && k <= 0x7e),
14639        b"space" => eq(b' ') || eq(b'\t') || eq(b'\n') || eq(b'\r') || eq(0x0c) || eq(0x0b),
14640        b"upper" => in_range(b'A', b'Z'),
14641        b"xdigit" => in_range(b'0', b'9') || in_range(b'A', b'F') || in_range(b'a', b'f'),
14642        _ => false,
14643    }
14644}
14645
14646/// Match a bracket expression [class] against char k.
14647unsafe fn match_bracket(mut p: *const u8, k: u8, kfold: u8) -> bool {
14648    let mut inv = false;
14649    p = p.add(1); // skip [
14650    if *p == b'^' || *p == b'!' {
14651        inv = true;
14652        p = p.add(1);
14653    }
14654    // handle ] as first char
14655    if *p == b']' {
14656        if k == b']' { return !inv; }
14657        p = p.add(1);
14658    } else if *p == b'-' {
14659        if k == b'-' { return !inv; }
14660        p = p.add(1);
14661    }
14662    while *p != b']' && *p != 0 {
14663        if *p == b'-' && *p.add(1) != b']' && *p.add(1) != 0 {
14664            // range: previous char to next char
14665            let lo = *p.sub(1);
14666            let hi = *p.add(1);
14667            if lo <= hi && (k >= lo && k <= hi || kfold >= lo && kfold <= hi) {
14668                return !inv;
14669            }
14670            p = p.add(2);
14671        } else if *p == b'[' && (*p.add(1) == b':' || *p.add(1) == b'.' || *p.add(1) == b'=') {
14672            let z = *p.add(1);
14673            let start = p.add(2);
14674            let mut q = start;
14675            while *q != 0 && (*q != z || *q.add(1) != b']') { q = q.add(1); }
14676            if *q != 0 {
14677                let len = (q as usize) - (start as usize);
14678                if len > 0 {
14679                    let inner = core::slice::from_raw_parts(start, len);
14680                    let matched =
14681                        if z == b':' { match_class(inner, k, kfold) }
14682                        else { len == 1 && (inner[0] == k || inner[0] == kfold) };
14683                    if matched { return !inv; }
14684                }
14685                p = q.add(2);
14686                continue;
14687            }
14688        } else {
14689            if *p == k || *p == kfold {
14690                return !inv;
14691            }
14692            p = p.add(1);
14693        }
14694    }
14695    inv
14696}
14697
14698/// Core fnmatch for a single path component (no FNM_PATHNAME splitting).
14699unsafe fn fnmatch_internal(pat: *const u8, str: *const u8, flags: c_int) -> c_int {
14700    // Handle leading .
14701    if flags & FNM_PERIOD != 0 && *str == b'.' && *pat != b'.' {
14702        return FNM_NOMATCH;
14703    }
14704
14705    let mut p = pat;
14706    let mut s = str;
14707
14708    // Consume pattern up to first *
14709    loop {
14710        let (tok, step) = pat_next(p, usize::MAX, flags);
14711        match tok {
14712            FNM_END => {
14713                return if *s == 0 { 0 } else { FNM_NOMATCH };
14714            }
14715            FNM_STAR => {
14716                p = p.add(1);
14717                break;
14718            }
14719            FNM_QUESTION => {
14720                if *s == 0 { return FNM_NOMATCH; }
14721                s = s.add(1);
14722                p = p.add(step);
14723            }
14724            FNM_BRACKET => {
14725                if *s == 0 { return FNM_NOMATCH; }
14726                let k = *s;
14727                let kfold = if flags & FNM_CASEFOLD != 0 { ascii_casefold(k) } else { k };
14728                if !match_bracket(p, k, kfold) { return FNM_NOMATCH; }
14729                p = p.add(step);
14730                s = s.add(1);
14731            }
14732            c => {
14733                if *s == 0 { return FNM_NOMATCH; }
14734                let k = *s;
14735                let kfold = if flags & FNM_CASEFOLD != 0 { ascii_casefold(k) } else { k };
14736                if k as c_int != c && kfold as c_int != c { return FNM_NOMATCH; }
14737                p = p.add(step);
14738                s = s.add(1);
14739            }
14740        }
14741    }
14742
14743    // Now p points after first *, rest of pattern is variable
14744    // Factor: find tail after last * and count fixed chars needed
14745    let mut ptail = p;
14746    let mut tailcnt: usize = 0;
14747    let mut pp = p;
14748    loop {
14749        let (tok, step) = pat_next(pp, usize::MAX, flags);
14750        if tok == FNM_END { break; }
14751        if tok == FNM_STAR {
14752            tailcnt = 0;
14753            ptail = pp.add(1);
14754        } else {
14755            tailcnt += 1;
14756        }
14757        pp = pp.add(step);
14758    }
14759
14760    // str must have at least tailcnt chars
14761    let slen = strlen(s as *const c_char);
14762    if slen < tailcnt { return FNM_NOMATCH; }
14763
14764    // Match tail: ptail..end against str[tailcnt from end]
14765    let stail = s.add(slen - tailcnt);
14766    let mut tp = ptail;
14767    let mut ts = stail;
14768    loop {
14769        let (tok, step) = pat_next(tp, usize::MAX, flags);
14770        tp = tp.add(step);
14771        if tok == FNM_END {
14772            break;
14773        }
14774        let k = *ts;
14775        if k == 0 { return FNM_NOMATCH; }
14776        let kfold = if flags & FNM_CASEFOLD != 0 { ascii_casefold(k) } else { k };
14777        match tok {
14778            FNM_QUESTION => { ts = ts.add(1); }
14779            FNM_BRACKET => {
14780                if !match_bracket(tp.sub(step), k, kfold) { return FNM_NOMATCH; }
14781                ts = ts.add(1);
14782            }
14783            c => {
14784                if k as c_int != c && kfold as c_int != c { return FNM_NOMATCH; }
14785                ts = ts.add(1);
14786            }
14787        }
14788    }
14789
14790    let endstr = stail;
14791    let endpat = ptail;
14792
14793    let mut cp = p;
14794    let mut cs = s;
14795    while cp < endpat {
14796        let (tok, step) = pat_next(cp, usize::MAX, flags);
14797        if tok == FNM_STAR {
14798            cp = cp.add(step);
14799            cs = s;
14800            continue;
14801        }
14802        let comp_start = cp;
14803        let mut ok = false;
14804        let mut try_s = cs;
14805        while try_s < endstr {
14806            let mut tmp_p = comp_start;
14807            let mut tmp_s = try_s;
14808            let mut matched = true;
14809            loop {
14810                let (t, st) = pat_next(tmp_p, usize::MAX, flags);
14811                if t == FNM_STAR || t == FNM_END {
14812                    cp = tmp_p;
14813                    cs = tmp_s;
14814                    ok = true;
14815                    break;
14816                }
14817                if *tmp_s == 0 {
14818                    matched = false;
14819                    break;
14820                }
14821                let k = *tmp_s;
14822                let kfold = if flags & FNM_CASEFOLD != 0 { ascii_casefold(k) } else { k };
14823                match t {
14824                    FNM_QUESTION => {
14825                        tmp_s = tmp_s.add(1);
14826                    }
14827                    FNM_BRACKET => {
14828                        if !match_bracket(tmp_p, k, kfold) {
14829                            matched = false;
14830                            break;
14831                        }
14832                        tmp_s = tmp_s.add(1);
14833                    }
14834                    c => {
14835                        if k as c_int != c && kfold as c_int != c {
14836                            matched = false;
14837                            break;
14838                        }
14839                        tmp_s = tmp_s.add(1);
14840                    }
14841                }
14842                tmp_p = tmp_p.add(st);
14843            }
14844            if ok { break; }
14845            if !matched {
14846                try_s = try_s.add(1);
14847            }
14848        }
14849        if !ok {
14850            return FNM_NOMATCH;
14851        }
14852    }
14853    0
14854}
14855
14856#[no_mangle]
14857pub unsafe extern "C" fn fnmatch(pat: *const c_char, str: *const c_char, flags: c_int) -> c_int {
14858    let pat = pat as *const u8;
14859    let str = str as *const u8;
14860
14861    if flags & FNM_PATHNAME != 0 {
14862        // Split on /
14863        let mut s = str;
14864        let mut p = pat;
14865        loop {
14866            // find next / in str
14867            let mut send = s;
14868            while *send != 0 && *send != b'/' { send = send.add(1); }
14869            // find next / in pat
14870            let mut pend = p;
14871            loop {
14872                let (tok, step) = pat_next(pend, usize::MAX, flags);
14873                if tok == FNM_END || (tok >= 0 && tok as u8 == b'/') {
14874                    break;
14875                }
14876                pend = pend.add(step);
14877            }
14878            let (ptok, pstep) = pat_next(pend, usize::MAX, flags);
14879            let pat_is_slash = ptok >= 0 && ptok as u8 == b'/';
14880            let str_is_slash = *send == b'/';
14881            if pat_is_slash != str_is_slash && (*send != 0 || !(flags & FNM_LEADING_DIR != 0)) {
14882                return FNM_NOMATCH;
14883            }
14884            // match component
14885            // We need a null-terminated copy of the component
14886            let plen = (pend as usize) - (p as usize);
14887            let slen = (send as usize) - (s as usize);
14888            // Use stack buffers for components
14889            // ponytail: max component 1024 bytes
14890            if plen > 1024 || slen > 1024 { return FNM_NOMATCH; }
14891            let mut pbuf = [0u8; 1025];
14892            let mut sbuf = [0u8; 1025];
14893            core::ptr::copy_nonoverlapping(p, pbuf.as_mut_ptr(), plen);
14894            pbuf[plen] = 0;
14895            core::ptr::copy_nonoverlapping(s, sbuf.as_mut_ptr(), slen);
14896            sbuf[slen] = 0;
14897            if fnmatch_internal(pbuf.as_ptr(), sbuf.as_ptr(), flags & !FNM_LEADING_DIR) != 0 {
14898                return FNM_NOMATCH;
14899            }
14900            if !pat_is_slash {
14901                return 0;
14902            }
14903            s = send.add(1);
14904            p = pend.add(pstep);
14905        }
14906    }
14907
14908    if flags & FNM_LEADING_DIR != 0 {
14909        // Try matching at each /
14910        let mut s = str;
14911        while *s != 0 {
14912            if *s == b'/' {
14913                // temporarily null-terminate
14914                // ponytail: we need to copy. Use a simple approach.
14915                let len = (s as usize) - (str as usize);
14916                // Just call fnmatch_internal with a temp buffer
14917                if len < 4096 {
14918                    let mut buf = [0u8; 4096];
14919                    core::ptr::copy_nonoverlapping(str, buf.as_mut_ptr(), len);
14920                    buf[len] = 0;
14921                    if fnmatch_internal(pat, buf.as_ptr(), flags) == 0 {
14922                        return 0;
14923                    }
14924                }
14925            }
14926            s = s.add(1);
14927        }
14928    }
14929
14930    fnmatch_internal(pat, str, flags)
14931}
14932
14933// ============================================================
14934// mntent: setmntent/endmntent/getmntent/getmntent_r/addmntent/hasmntopt
14935// ============================================================
14936
14937#[no_mangle]
14938pub unsafe extern "C" fn setmntent(name: *const c_char, mode: *const c_char) -> *mut FILE {
14939    fopen(name, mode)
14940}
14941
14942#[no_mangle]
14943pub unsafe extern "C" fn endmntent(f: *mut FILE) -> c_int {
14944    if !f.is_null() {
14945        fclose(f);
14946    }
14947    1
14948}
14949
14950// ponytail: static buffers for getmntent (non-_r variant)
14951static mut MNTENT_MNT: MntEnt = MntEnt {
14952    mnt_fsname: core::ptr::null_mut(),
14953    mnt_dir: core::ptr::null_mut(),
14954    mnt_type: core::ptr::null_mut(),
14955    mnt_opts: core::ptr::null_mut(),
14956    mnt_freq: 0,
14957    mnt_passno: 0,
14958};
14959
14960#[repr(C)]
14961pub struct MntEnt {
14962    mnt_fsname: *mut c_char,
14963    mnt_dir: *mut c_char,
14964    mnt_type: *mut c_char,
14965    mnt_opts: *mut c_char,
14966    mnt_freq: c_int,
14967    mnt_passno: c_int,
14968}
14969
14970unsafe fn unescape_ent(beg: *mut c_char) -> *mut c_char {
14971    let mut dest = beg;
14972    let mut src = beg;
14973    while *src != 0 {
14974        if *src != b'\\' as c_char {
14975            *dest = *src;
14976            dest = dest.add(1);
14977            src = src.add(1);
14978            continue;
14979        }
14980        // backslash
14981        if *src.add(1) == b'\\' as c_char {
14982            src = src.add(1);
14983            *dest = *src;
14984            dest = dest.add(1);
14985            src = src.add(1);
14986            continue;
14987        }
14988        // octal escape
14989        let mut val: u8 = 0;
14990        let mut cnt = 0;
14991        let mut sp = src.add(1);
14992        while cnt < 3 && *sp >= b'0' as c_char && *sp <= b'7' as c_char {
14993            val = val * 8 + (*sp as u8 - b'0');
14994            sp = sp.add(1);
14995            cnt += 1;
14996        }
14997        if cnt > 0 {
14998            *dest = val as c_char;
14999            dest = dest.add(1);
15000            src = sp;
15001        } else {
15002            *dest = *src;
15003            dest = dest.add(1);
15004            src = src.add(1);
15005        }
15006    }
15007    *dest = 0;
15008    beg
15009}
15010
15011unsafe fn parse_mntent_line(
15012    linebuf: *mut c_char,
15013    mnt: *mut MntEnt,
15014) -> *mut MntEnt {
15015    let _len = strlen(linebuf as *const c_char);
15016    // skip comments and empty lines
15017    // parse: fsname dir type opts freq passno
15018    let mut p = linebuf;
15019    // skip leading whitespace
15020    while *p == b' ' as c_char || *p == b'\t' as c_char || *p == b'\n' as c_char || *p == b'\r' as c_char {
15021        p = p.add(1);
15022    }
15023    if *p == 0 || *p == b'#' as c_char {
15024        return core::ptr::null_mut();
15025    }
15026
15027    // fsname
15028    (*mnt).mnt_fsname = p;
15029    while *p != 0 && *p != b' ' as c_char && *p != b'\t' as c_char && *p != b'\n' as c_char {
15030        p = p.add(1);
15031    }
15032    if *p == 0 { return core::ptr::null_mut(); }
15033    *p = 0;
15034    p = p.add(1);
15035    while *p == b' ' as c_char || *p == b'\t' as c_char { p = p.add(1); }
15036
15037    // dir
15038    (*mnt).mnt_dir = p;
15039    while *p != 0 && *p != b' ' as c_char && *p != b'\t' as c_char && *p != b'\n' as c_char {
15040        p = p.add(1);
15041    }
15042    if *p == 0 { return core::ptr::null_mut(); }
15043    *p = 0;
15044    p = p.add(1);
15045    while *p == b' ' as c_char || *p == b'\t' as c_char { p = p.add(1); }
15046
15047    // type
15048    (*mnt).mnt_type = p;
15049    while *p != 0 && *p != b' ' as c_char && *p != b'\t' as c_char && *p != b'\n' as c_char {
15050        p = p.add(1);
15051    }
15052    if *p == 0 { return core::ptr::null_mut(); }
15053    *p = 0;
15054    p = p.add(1);
15055    while *p == b' ' as c_char || *p == b'\t' as c_char { p = p.add(1); }
15056
15057    // opts
15058    (*mnt).mnt_opts = p;
15059    while *p != 0 && *p != b' ' as c_char && *p != b'\t' as c_char && *p != b'\n' as c_char {
15060        p = p.add(1);
15061    }
15062    if *p != 0 {
15063        *p = 0;
15064        p = p.add(1);
15065    }
15066    if *(*mnt).mnt_opts == 0 {
15067        (*mnt).mnt_opts = b"defaults\0".as_ptr() as *mut c_char;
15068    }
15069
15070    // freq and passno
15071    (*mnt).mnt_freq = 0;
15072    (*mnt).mnt_passno = 0;
15073    // skip whitespace
15074    while *p == b' ' as c_char || *p == b'\t' as c_char { p = p.add(1); }
15075    if *p >= b'0' as c_char && *p <= b'9' as c_char {
15076        (*mnt).mnt_freq = (*p - b'0' as c_char) as c_int;
15077        p = p.add(1);
15078        while *p >= b'0' as c_char && *p <= b'9' as c_char {
15079            (*mnt).mnt_freq = (*mnt).mnt_freq * 10 + (*p - b'0' as c_char) as c_int;
15080            p = p.add(1);
15081        }
15082    }
15083    while *p == b' ' as c_char || *p == b'\t' as c_char { p = p.add(1); }
15084    if *p >= b'0' as c_char && *p <= b'9' as c_char {
15085        (*mnt).mnt_passno = (*p - b'0' as c_char) as c_int;
15086        p = p.add(1);
15087        while *p >= b'0' as c_char && *p <= b'9' as c_char {
15088            (*mnt).mnt_passno = (*mnt).mnt_passno * 10 + (*p - b'0' as c_char) as c_int;
15089            p = p.add(1);
15090        }
15091    }
15092
15093    // unescape
15094    unescape_ent((*mnt).mnt_fsname);
15095    unescape_ent((*mnt).mnt_dir);
15096    unescape_ent((*mnt).mnt_type);
15097    unescape_ent((*mnt).mnt_opts);
15098
15099    mnt
15100}
15101
15102// Use a sentinel pointer to detect internal buffer usage
15103// ponytail: cast integer to pointer for sentinel
15104static SENTINEL_PTR: usize = 1;
15105
15106// Read one char from FILE buffer directly (avoids musl's static fgets ABI mismatch)
15107unsafe fn mntent_fgetc(f: *mut FILE) -> c_int {
15108    fgetc(f)
15109}
15110
15111unsafe fn mntent_fgets(buf: *mut c_char, n: usize, f: *mut FILE) -> *mut c_char {
15112    if n == 0 { return core::ptr::null_mut(); }
15113    let max = n - 1;
15114    let mut i = 0usize;
15115    while i < max {
15116        let c = mntent_fgetc(f);
15117        if c == -1 { if i == 0 { return core::ptr::null_mut(); } break; }
15118        *buf.add(i) = c as c_char;
15119        i += 1;
15120        if c == b'\n' as c_int { break; }
15121    }
15122    *buf.add(i) = 0;
15123    buf
15124}
15125
15126#[no_mangle]
15127pub unsafe extern "C" fn getmntent_r(
15128    f: *mut FILE,
15129    mnt: *mut MntEnt,
15130    mut linebuf: *mut c_char,
15131    _buflen: c_int,
15132) -> *mut MntEnt {
15133    let use_internal = linebuf as usize == SENTINEL_PTR;
15134
15135    (*mnt).mnt_freq = 0;
15136    (*mnt).mnt_passno = 0;
15137
15138    // ponytail: static 4KB line buffer for getmntent
15139    static mut INTERNAL_BUF: [c_char; 4096] = [0; 4096];
15140
15141    loop {
15142        if use_internal {
15143            linebuf = core::ptr::addr_of_mut!(INTERNAL_BUF).cast::<c_char>();
15144        }
15145        let res = mntent_fgets(linebuf, 4096, f);
15146        if res.is_null() {
15147            return core::ptr::null_mut();
15148        }
15149        let mut has_newline = false;
15150        let mut p = linebuf;
15151        while *p != 0 { if *p == b'\n' as c_char { has_newline = true; break; } p = p.add(1); }
15152        if !has_newline {
15153            ERRNO!() = 34;
15154            return core::ptr::null_mut();
15155        }
15156        let result = parse_mntent_line(linebuf, mnt);
15157        if !result.is_null() {
15158            return result;
15159        }
15160    }
15161}
15162
15163#[no_mangle]
15164pub unsafe extern "C" fn getmntent(f: *mut FILE) -> *mut MntEnt {
15165    getmntent_r(f, core::ptr::addr_of_mut!(MNTENT_MNT), SENTINEL_PTR as *mut c_char, 0)
15166}
15167
15168#[no_mangle]
15169pub unsafe extern "C" fn addmntent(f: *mut FILE, mnt: *const MntEnt) -> c_int {
15170    fseek(f, 0, 2); // SEEK_END
15171    let written = fprintf(
15172        f,
15173        b"%s\t%s\t%s\t%s\t%d\t%d\n\0".as_ptr() as *const c_char,
15174        (*mnt).mnt_fsname,
15175        (*mnt).mnt_dir,
15176        (*mnt).mnt_type,
15177        (*mnt).mnt_opts,
15178        (*mnt).mnt_freq,
15179        (*mnt).mnt_passno,
15180    );
15181    if written < 0 { 1 } else { 0 }
15182}
15183
15184#[no_mangle]
15185pub unsafe extern "C" fn hasmntopt(mnt: *const MntEnt, opt: *const c_char) -> *mut c_char {
15186    let l = strlen(opt as *const c_char);
15187    let mut p = (*mnt).mnt_opts;
15188    loop {
15189        if strncmp(p as *const u8, opt as *const u8, l) == 0 {
15190            let after = *p.add(l);
15191            if after == 0 || after == b',' as c_char || after == b'=' as c_char {
15192                return p;
15193            }
15194        }
15195        let comma = strchr(p as *const u8, b',' as c_int);
15196        if comma.is_null() {
15197            return core::ptr::null_mut();
15198        }
15199        p = comma.add(1) as *mut c_char;
15200    }
15201}
15202
15203// ============================================================
15204// SysV IPC: ftok, msg*, sem*, shm*
15205// x86_64 direct syscalls: msgget=68, msgsnd=69, msgrcv=70, msgctl=71
15206//   semget=64, semop=65, semctl=66, semtimedop=220
15207//   shmget=29, shmat=30, shmctl=31, shmdt=67
15208// ============================================================
15209
15210const EEXIST: c_int = 17;
15211const ENAMETOOLONG: c_int = 36;
15212const EMFILE: c_int = 24;
15213const EIDRM: c_int = 43;
15214
15215#[inline]
15216unsafe fn sys_msgget(key: c_int, msgflg: c_int) -> i64 {
15217    <Arch as Syscalls>::syscall2(SYS_MSGGET, key as i64, msgflg as i64)
15218}
15219
15220#[inline]
15221unsafe fn sys_msgsnd(msqid: c_int, msgp: *const c_void, msgsz: usize, msgflg: c_int) -> i64 {
15222    <Arch as Syscalls>::syscall4(SYS_MSGSND, msqid as i64, msgp as i64, msgsz as i64, msgflg as i64)
15223}
15224
15225#[inline]
15226unsafe fn sys_msgrcv(msqid: c_int, msgp: *mut c_void, msgsz: usize, msgtyp: c_long, msgflg: c_int) -> i64 {
15227    <Arch as Syscalls>::syscall5(SYS_MSGRCV, msqid as i64, msgp as i64, msgsz as i64, msgtyp as i64, msgflg as i64)
15228}
15229
15230#[inline]
15231unsafe fn sys_msgctl(msqid: c_int, cmd: c_int, buf: *mut c_void) -> i64 {
15232    <Arch as Syscalls>::syscall3(SYS_MSGCTL, msqid as i64, cmd as i64, buf as i64)
15233}
15234
15235#[inline]
15236unsafe fn sys_semget(key: c_int, nsems: c_int, semflg: c_int) -> i64 {
15237    <Arch as Syscalls>::syscall3(SYS_SEMGET, key as i64, nsems as i64, semflg as i64)
15238}
15239
15240#[inline]
15241unsafe fn sys_semop(semid: c_int, sops: *const c_void, nsops: usize) -> i64 {
15242    <Arch as Syscalls>::syscall3(SYS_SEMOP, semid as i64, sops as i64, nsops as i64)
15243}
15244
15245#[inline]
15246unsafe fn sys_semctl(semid: c_int, semnum: c_int, cmd: c_int, arg: *mut c_void) -> i64 {
15247    <Arch as Syscalls>::syscall4(SYS_SEMCTL, semid as i64, semnum as i64, cmd as i64, arg as i64)
15248}
15249
15250#[inline]
15251unsafe fn sys_shmget(key: c_int, size: usize, shmflg: c_int) -> i64 {
15252    <Arch as Syscalls>::syscall3(SYS_SHMGET, key as i64, size as i64, shmflg as i64)
15253}
15254
15255#[inline]
15256unsafe fn sys_shmat(shmid: c_int, shmaddr: *const c_void, shmflg: c_int) -> i64 {
15257    <Arch as Syscalls>::syscall3(SYS_SHMAT, shmid as i64, shmaddr as i64, shmflg as i64)
15258}
15259
15260#[inline]
15261unsafe fn sys_shmdt(shmaddr: *const c_void) -> i64 {
15262    <Arch as Syscalls>::syscall1(SYS_SHMDT, shmaddr as i64)
15263}
15264
15265#[inline]
15266unsafe fn sys_shmctl(shmid: c_int, cmd: c_int, buf: *mut c_void) -> i64 {
15267    <Arch as Syscalls>::syscall3(SYS_SHMCTL, shmid as i64, cmd as i64, buf as i64)
15268}
15269
15270// ftok
15271#[no_mangle]
15272pub unsafe extern "C" fn ftok(path: *const c_char, id: c_int) -> c_int {
15273    let mut st: kernel_stat64 = core::mem::zeroed();
15274    let r = sys_stat(path as *const u8, &mut st);
15275    if r < 0 {
15276        ERRNO!() = (-r) as c_int;
15277        return -1;
15278    }
15279    ((st.st_ino & 0xffff) | ((st.st_dev & 0xff) << 16) | (((id as u32 & 0xff) << 24)) as u64) as c_int
15280}
15281
15282// msgget
15283#[no_mangle]
15284pub unsafe extern "C" fn msgget(key: c_int, msgflg: c_int) -> c_int {
15285    let r = sys_msgget(key, msgflg);
15286    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { r as c_int }
15287}
15288
15289// msgsnd
15290#[no_mangle]
15291pub unsafe extern "C" fn msgsnd(msqid: c_int, msgp: *const c_void, msgsz: usize, msgflg: c_int) -> c_int {
15292    let r = sys_msgsnd(msqid, msgp, msgsz, msgflg);
15293    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { r as c_int }
15294}
15295
15296// msgrcv
15297#[no_mangle]
15298pub unsafe extern "C" fn msgrcv(msqid: c_int, msgp: *mut c_void, msgsz: usize, msgtyp: c_long, msgflg: c_int) -> isize {
15299    let r = sys_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg);
15300    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { r as isize }
15301}
15302
15303// msgctl
15304#[no_mangle]
15305pub unsafe extern "C" fn msgctl(msqid: c_int, cmd: c_int, buf: *mut c_void) -> c_int {
15306    let r = sys_msgctl(msqid, cmd, buf);
15307    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { r as c_int }
15308}
15309
15310// semget
15311#[no_mangle]
15312pub unsafe extern "C" fn semget(key: c_int, nsems: c_int, semflg: c_int) -> c_int {
15313    if nsems > 65535 { ERRNO!() = 22; return -1; }
15314    let r = sys_semget(key, nsems, semflg);
15315    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { r as c_int }
15316}
15317
15318// semop
15319#[no_mangle]
15320pub unsafe extern "C" fn semop(semid: c_int, sops: *const c_void, nsops: usize) -> c_int {
15321    let r = sys_semop(semid, sops, nsops);
15322    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { r as c_int }
15323}
15324
15325// semctl - C variadic
15326#[no_mangle]
15327pub unsafe extern "C" fn semctl(semid: c_int, semnum: c_int, cmd: c_int, mut args: ...) -> c_int {
15328    let mut arg: *mut c_void = core::ptr::null_mut();
15329    // Commands that take the union semun argument
15330    match cmd {
15331        // SETVAL/SETALL/GETALL, IPC_SET/IPC_STAT/IPC_INFO, SEM_STAT,
15332        // SEM_INFO, SEM_STAT_ANY and the 32-bit compat variants all carry
15333        // the `union semun` argument.
15334        16 | 13 | 17 | 1 | 2 | 3 | 18 | 19 | 20 | 0x102 | 0x104 | 0x106 => {
15335            arg = args.next_arg::<*mut c_void>();
15336        }
15337        _ => {}
15338    }
15339    let r = sys_semctl(semid, semnum, cmd, arg);
15340    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { r as c_int }
15341}
15342
15343// shmget
15344#[no_mangle]
15345pub unsafe extern "C" fn shmget(key: c_int, size: usize, shmflg: c_int) -> c_int {
15346    let r = sys_shmget(key, size, shmflg);
15347    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { r as c_int }
15348}
15349
15350// shmat
15351#[no_mangle]
15352pub unsafe extern "C" fn shmat(shmid: c_int, shmaddr: *const c_void, shmflg: c_int) -> *mut c_void {
15353    let r = sys_shmat(shmid, shmaddr, shmflg);
15354    if (r as usize) > (-(4096isize as isize)) as usize {
15355        ERRNO!() = (-r) as c_int;
15356        return -1isize as *mut c_void;
15357    }
15358    r as *mut c_void
15359}
15360
15361// shmdt
15362#[no_mangle]
15363pub unsafe extern "C" fn shmdt(shmaddr: *const c_void) -> c_int {
15364    let r = sys_shmdt(shmaddr);
15365    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { r as c_int }
15366}
15367
15368// shmctl
15369#[no_mangle]
15370pub unsafe extern "C" fn shmctl(shmid: c_int, cmd: c_int, buf: *mut c_void) -> c_int {
15371    let r = sys_shmctl(shmid, cmd, buf);
15372    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { r as c_int }
15373}
15374
15375// ============================================================
15376// Startup: __libc_start_main
15377//
15378// musl crt1.o _start_c calls:
15379//   __libc_start_main(main, argc, argv, _init, _fini, rtld_fini, stack_end)
15380// ============================================================
15381
15382// Auxiliary vector pointer, populated by the dynamic linker before
15383// constructors run so getauxval() works during early startup.
15384#[no_mangle]
15385pub static mut __auxv: *const usize = core::ptr::null();
15386#[no_mangle]
15387pub static mut __rc_static_base: usize = 0;
15388static mut RC_STATIC_EXEC: bool = false;
15389
15390#[no_mangle]
15391pub static mut __progname: *mut c_char = core::ptr::null_mut();
15392#[no_mangle]
15393pub static mut __progname_full: *mut c_char = core::ptr::null_mut();
15394#[no_mangle]
15395pub static mut program_invocation_name: *mut c_char = core::ptr::null_mut();
15396#[no_mangle]
15397pub static mut program_invocation_short_name: *mut c_char = core::ptr::null_mut();
15398
15399#[no_mangle]
15400pub static in6addr_any: [u32; 4] = [0; 4];
15401#[no_mangle]
15402pub static in6addr_loopback: [u32; 4] = [0, 0, 0, 0x0100_0000];
15403
15404#[no_mangle]
15405pub static mut __daylight: c_int = 0;
15406#[no_mangle]
15407pub static mut __timezone: c_long = 0;
15408#[no_mangle]
15409pub static mut __tzname: [*mut c_char; 2] = [core::ptr::null_mut(); 2];
15410
15411#[no_mangle]
15412pub unsafe extern "C" fn getauxval(type_: c_ulong) -> c_ulong {
15413    let mut p = __auxv;
15414    if p.is_null() {
15415        return 0;
15416    }
15417    loop {
15418        let tag = *p;
15419        if tag == 0 {
15420            return 0;
15421        }
15422        if tag == type_ as usize {
15423            return *p.add(1) as c_ulong;
15424        }
15425        p = p.add(2);
15426    }
15427}
15428
15429
15430unsafe fn run_static_init() {
15431    if !RC_STATIC_EXEC {
15432        return;
15433    }
15434    extern "C" {
15435        static __preinit_array_start: usize;
15436        static __preinit_array_end: usize;
15437        static __init_array_start: usize;
15438        static __init_array_end: usize;
15439    }
15440    let mut p = core::ptr::addr_of!(__preinit_array_start) as usize;
15441    let end = core::ptr::addr_of!(__preinit_array_end) as usize;
15442    while p < end {
15443        let f = core::ptr::read_unaligned(p as *const usize);
15444        if f != 0 {
15445            let f: unsafe extern "C" fn() = core::mem::transmute(f);
15446            f();
15447        }
15448        p += core::mem::size_of::<usize>();
15449    }
15450    p = core::ptr::addr_of!(__init_array_start) as usize;
15451    let end = core::ptr::addr_of!(__init_array_end) as usize;
15452    while p < end {
15453        let f = core::ptr::read_unaligned(p as *const usize);
15454        if f != 0 {
15455            let f: unsafe extern "C" fn() = core::mem::transmute(f);
15456            f();
15457        }
15458        p += core::mem::size_of::<usize>();
15459    }
15460}
15461
15462type MainFn = unsafe extern "C" fn(c_int, *const *const c_char, *const *const c_char) -> c_int;
15463type InitFn = unsafe extern "C" fn();
15464
15465#[cfg(target_arch = "x86_64")]
15466unsafe fn static_tls_tp() -> usize {
15467    let mut fs: u64 = 0;
15468    if arch_prctl(0x1003, &mut fs as *mut u64 as u64) != 0 {
15469        0
15470    } else {
15471        fs as usize
15472    }
15473}
15474
15475#[cfg(target_arch = "x86_64")]
15476unsafe fn set_static_tls_tp(tp: usize) {
15477    let _ = arch_prctl(0x1002, tp as u64);
15478}
15479
15480#[cfg(target_arch = "aarch64")]
15481unsafe fn static_tls_tp() -> usize {
15482    let tp: usize;
15483    core::arch::asm!("mrs {}, tpidr_el0", out(reg) tp, options(nostack));
15484    tp
15485}
15486
15487#[cfg(target_arch = "aarch64")]
15488unsafe fn set_static_tls_tp(tp: usize) {
15489    core::arch::asm!("msr tpidr_el0, {}", in(reg) tp, options(nostack));
15490}
15491
15492#[cfg(target_arch = "riscv64")]
15493unsafe fn static_tls_tp() -> usize {
15494    let tp: usize;
15495    core::arch::asm!("mv {}, tp", out(reg) tp, options(nostack));
15496    tp
15497}
15498
15499#[cfg(target_arch = "riscv64")]
15500unsafe fn set_static_tls_tp(tp: usize) {
15501    core::arch::asm!("mv tp, {}", in(reg) tp, options(nostack));
15502}
15503
15504static mut STATIC_TLS_BLOCK_SIZE: usize = 0;
15505static mut STATIC_TLS_TP_OFFSET: usize = 0;
15506static mut STATIC_TLS_VAR_OFFSET: usize = 0;
15507static mut STATIC_TLS_TLS_SIZE: usize = 0;
15508static mut STATIC_TLS_IMAGE: *const u8 = core::ptr::null();
15509static mut STATIC_TLS_FILESZ: usize = 0;
15510static mut STATIC_TLS_CANARY: u64 = 0;
15511
15512unsafe fn static_tls_init_block(block: *mut u8) -> *mut u8 {
15513    if block.is_null() || STATIC_TLS_BLOCK_SIZE == 0 {
15514        return core::ptr::null_mut();
15515    }
15516    let var_base = block.add(STATIC_TLS_VAR_OFFSET);
15517    if STATIC_TLS_TLS_SIZE > 0 {
15518        core::ptr::write_bytes(var_base, 0, STATIC_TLS_TLS_SIZE);
15519    }
15520    if STATIC_TLS_FILESZ > 0 && !STATIC_TLS_IMAGE.is_null() {
15521        core::ptr::copy_nonoverlapping(STATIC_TLS_IMAGE, var_base, STATIC_TLS_FILESZ);
15522    }
15523    let tp = block.add(STATIC_TLS_TP_OFFSET);
15524    let tcb = if cfg!(target_arch = "x86_64") {
15525        tp as usize
15526    } else {
15527        block as usize
15528    };
15529    core::ptr::write_unaligned(tcb as *mut u64, tcb as u64);
15530    core::ptr::write_unaligned((tcb as *mut u64).add(1), 1);
15531    core::ptr::write_unaligned((tcb as *mut u64).add(2), STATIC_TLS_TLS_SIZE as u64);
15532    core::ptr::write_unaligned((tcb as *mut u64).add(3), 1);
15533    core::ptr::write_unaligned((tcb as *mut u64).add(4), 0); // reserved
15534    core::ptr::write_unaligned((tcb as *mut u64).add(5), STATIC_TLS_CANARY); // 0x28
15535    core::ptr::write_unaligned((tcb as *mut u64).add(6), 0); // errno at 0x30
15536    tp
15537}
15538
15539unsafe fn static_tls_create_block() -> *mut u8 {
15540    if STATIC_TLS_BLOCK_SIZE == 0 {
15541        return core::ptr::null_mut();
15542    }
15543    let block = sys_mmap(
15544        core::ptr::null_mut(),
15545        STATIC_TLS_BLOCK_SIZE,
15546        PROT_READ | PROT_WRITE,
15547        MAP_PRIVATE | MAP_ANONYMOUS,
15548        -1,
15549        0,
15550    );
15551    if block.is_null() || block == MMAP_FAILED {
15552        return core::ptr::null_mut();
15553    }
15554    static_tls_init_block(block)
15555}
15556
15557#[repr(C)]
15558pub struct TlsIndex {
15559    pub ti_module: usize,
15560    pub ti_offset: usize,
15561}
15562
15563/// Global-dynamic TLS access.  The dynamic loader intercepts this symbol for
15564/// dynamically linked programs and uses its own multi-module implementation.
15565/// A static executable has only module 0, so resolve directly against the
15566/// static TLS block created by `init_static_tls()`.
15567static mut TLS_GET_ADDR_IMPL: usize = 0;
15568
15569/// The dynamic loader installs its multi-module TLS resolver here before
15570/// running constructors.  Keeping a dispatch pointer means a program that
15571/// obtains `__tls_get_addr` through `dlsym` still reaches the loader's
15572/// implementation rather than the static fallback below.
15573#[no_mangle]
15574pub unsafe extern "C" fn __rc_set_tls_get_addr(f: usize) {
15575    TLS_GET_ADDR_IMPL = f;
15576}
15577
15578#[no_mangle]
15579pub unsafe extern "C" fn __tls_get_addr(ti: *const TlsIndex) -> *mut u8 {
15580    if TLS_GET_ADDR_IMPL != 0 {
15581        let f: unsafe extern "C" fn(*const TlsIndex) -> *mut u8 =
15582            core::mem::transmute(TLS_GET_ADDR_IMPL);
15583        return f(ti);
15584    }
15585    if ti.is_null() || (*ti).ti_module != 0 || !RC_STATIC_EXEC {
15586        return core::ptr::null_mut();
15587    }
15588    let tp = static_tls_tp();
15589    if tp == 0 {
15590        return core::ptr::null_mut();
15591    }
15592    (tp - STATIC_TLS_TP_OFFSET + STATIC_TLS_VAR_OFFSET + (*ti).ti_offset) as *mut u8
15593}
15594
15595/// Static executables have no dynamic loader to build a TCB.  Allocate one
15596/// from PT_TLS so that `%fs`/TP-relative TLS accesses and errno work.
15597unsafe fn init_static_tls() {
15598    if static_tls_tp() != 0 {
15599        return;
15600    }
15601    let phdr = getauxval(3) as usize; // AT_PHDR
15602    let phnum = getauxval(5) as usize; // AT_PHNUM
15603    let phent = getauxval(4) as usize; // AT_PHENT
15604    if phdr == 0 || phnum == 0 || phent < 56 {
15605        return;
15606    }
15607
15608    let mut tls_vaddr = 0usize;
15609    let mut tls_filesz = 0usize;
15610    let mut tls_memsz = 0usize;
15611    let mut tls_align = 1usize;
15612    let mut exec_base = __rc_static_base;
15613    let mut i = 0usize;
15614    while i < phnum {
15615        let ph = phdr + i * phent;
15616        let p_type = core::ptr::read_unaligned(ph as *const u32);
15617        if p_type == 3 {
15618            // PT_INTERP means the dynamic loader already owns TLS.
15619            return;
15620        }
15621        if p_type == 6 {
15622            // PT_PHDR
15623            if exec_base == 0 {
15624                let p_vaddr = core::ptr::read_unaligned((ph + 16) as *const u64) as usize;
15625                exec_base = phdr.wrapping_sub(p_vaddr);
15626            }
15627        } else if p_type == 7 {
15628            // PT_TLS
15629            tls_vaddr = core::ptr::read_unaligned((ph + 16) as *const u64) as usize;
15630            tls_filesz = core::ptr::read_unaligned((ph + 32) as *const u64) as usize;
15631            tls_memsz = core::ptr::read_unaligned((ph + 40) as *const u64) as usize;
15632            let align = core::ptr::read_unaligned((ph + 48) as *const u64) as usize;
15633            if align > 1 {
15634                tls_align = align;
15635            }
15636        }
15637        i += 1;
15638    }
15639
15640    let tcb_size = 256usize;
15641    #[cfg(target_arch = "x86_64")]
15642    let gap = 0usize;
15643    #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
15644    let gap = if cfg!(target_arch = "aarch64") { 16usize } else { 0usize };
15645
15646    let tls_size = if tls_align > 1 {
15647        (tls_memsz + tls_align - 1) & !(tls_align - 1)
15648    } else {
15649        tls_memsz
15650    };
15651    let total = tcb_size + gap + tls_size + tls_align;
15652    STATIC_TLS_BLOCK_SIZE = total;
15653    STATIC_TLS_TP_OFFSET = if cfg!(target_arch = "x86_64") {
15654        tls_size
15655    } else {
15656        tcb_size
15657    };
15658    STATIC_TLS_VAR_OFFSET = if cfg!(target_arch = "x86_64") {
15659        0
15660    } else {
15661        tcb_size + gap
15662    };
15663    STATIC_TLS_TLS_SIZE = tls_size;
15664    STATIC_TLS_IMAGE = (exec_base + tls_vaddr) as *const u8;
15665    STATIC_TLS_FILESZ = tls_filesz;
15666    let random = getauxval(25) as *const u8; // AT_RANDOM
15667    STATIC_TLS_CANARY = if random.is_null() {
15668        0xdefaced_cafebeefu64
15669    } else {
15670        core::ptr::read_unaligned(random as *const u64)
15671            ^ core::ptr::read_unaligned(random.add(8) as *const u64)
15672    };
15673
15674    let block = sys_mmap(
15675        core::ptr::null_mut(),
15676        total,
15677        PROT_READ | PROT_WRITE,
15678        MAP_PRIVATE | MAP_ANONYMOUS,
15679        -1,
15680        0,
15681    );
15682    if block.is_null() || block == MMAP_FAILED {
15683        return;
15684    }
15685    let tp = static_tls_init_block(block);
15686    if tp.is_null() {
15687        return;
15688    }
15689    set_static_tls_tp(tp as usize);
15690    RC_STATIC_EXEC = true;
15691}
15692
15693
15694#[no_mangle]
15695pub unsafe extern "C" fn __libc_start_main(
15696    main: MainFn,
15697    argc: c_int,
15698    argv: *const *const c_char,
15699    _init: *const c_void,
15700    _fini: *const c_void,
15701    _rtld_fini: *const c_void,
15702    _stack_end: *const c_void,
15703) -> ! {
15704    let envp = argv.add((argc + 1) as usize);
15705    let mut aux = envp;
15706    while !(*aux).is_null() {
15707        aux = aux.add(1);
15708    }
15709    __auxv = aux.add(1) as *const usize;
15710    init_static_tls();
15711    errno_init();
15712
15713    __environ = envp as *mut *mut c_char;
15714    environ = __environ; // sync environ alias
15715    ___environ = __environ;
15716    _environ = __environ;
15717
15718    // glibc-compatible program name globals.
15719    if !argv.is_null() && argc > 0 {
15720        let argv0 = *argv;
15721        __progname_full = argv0 as *mut c_char;
15722        program_invocation_name = argv0 as *mut c_char;
15723        let mut base = argv0;
15724        let mut p = argv0;
15725        while *p != 0 {
15726            if *p == b'/' as c_char { base = p.add(1); }
15727            p = p.add(1);
15728        }
15729        __progname = base as *mut c_char;
15730        program_invocation_short_name = base as *mut c_char;
15731    }
15732
15733    __stdio_init();
15734    run_static_init();
15735
15736    let mut set: SigSetT = 0;
15737    sigemptyset(&mut set);
15738    sigaddset(&mut set, SIGCANCEL);
15739    sigprocmask(SIG_UNBLOCK, &set, core::ptr::null_mut());
15740
15741    if !_init.is_null() {
15742        let init_fn: InitFn = core::mem::transmute(_init);
15743        init_fn();
15744    }
15745
15746    // aarch64/riscv64 GCC reads __stack_chk_guard as a global (GOT-based for PIE),
15747    // so we must initialize it from AT_RANDOM before calling main.
15748    // x86_64 uses %fs:0x28 which the kernel already set up.
15749    #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
15750    {
15751        let random_ptr = getauxval(25) as *const u8; // AT_RANDOM = 25
15752        if !random_ptr.is_null() {
15753            let a = core::ptr::read_unaligned(random_ptr as *const u64);
15754            let b = core::ptr::read_unaligned(random_ptr.add(8) as *const u64);
15755            __stack_chk_guard = (a ^ b) as usize;
15756        } else {
15757            // ponytail: fallback entropy, not crypto-grade but nonzero
15758            __stack_chk_guard = 0xdefaced_cafebeef;
15759        }
15760    }
15761
15762    let result = main(argc, argv, envp);
15763
15764    if !_fini.is_null() {
15765        let fini_fn: InitFn = core::mem::transmute(_fini);
15766        fini_fn();
15767    }
15768
15769    exit(result);
15770}
15771
15772type LdsoDlopenFn = unsafe extern "C" fn(*const c_char, c_int) -> *mut c_void;
15773type LdsoDlsymFn = unsafe extern "C" fn(*mut c_void, *const c_char, *mut c_void) -> *mut c_void;
15774type LdsoDlcloseFn = unsafe extern "C" fn(*mut c_void) -> c_int;
15775type LdsoDlerrorFn = unsafe extern "C" fn() -> *const c_char;
15776
15777static mut LDSO_DLOPEN: Option<LdsoDlopenFn> = None;
15778static mut LDSO_DLSYM: Option<LdsoDlsymFn> = None;
15779static mut LDSO_DLCLOSE: Option<LdsoDlcloseFn> = None;
15780static mut LDSO_DLERROR: Option<LdsoDlerrorFn> = None;
15781
15782#[no_mangle]
15783pub unsafe extern "C" fn __ldso_register_dlopen(f: LdsoDlopenFn) {
15784    LDSO_DLOPEN = Some(f);
15785}
15786
15787#[no_mangle]
15788pub unsafe extern "C" fn __ldso_register_dlsym(f: LdsoDlsymFn) {
15789    LDSO_DLSYM = Some(f);
15790}
15791
15792#[no_mangle]
15793pub unsafe extern "C" fn __ldso_register_dlclose(f: LdsoDlcloseFn) {
15794    LDSO_DLCLOSE = Some(f);
15795}
15796
15797#[no_mangle]
15798pub unsafe extern "C" fn __ldso_register_dlerror(f: LdsoDlerrorFn) {
15799    LDSO_DLERROR = Some(f);
15800}
15801
15802#[no_mangle]
15803pub unsafe extern "C" fn dlopen(filename: *const c_char, flags: c_int) -> *mut c_void {
15804    if let Some(f) = LDSO_DLOPEN {
15805        f(filename, flags)
15806    } else {
15807        null_mut()
15808    }
15809}
15810
15811#[no_mangle]
15812pub unsafe extern "C" fn dlsym_impl(
15813    handle: *mut c_void,
15814    symbol: *const c_char,
15815    ra: *mut c_void,
15816) -> *mut c_void {
15817    if let Some(f) = LDSO_DLSYM {
15818        f(handle, symbol, ra)
15819    } else {
15820        null_mut()
15821    }
15822}
15823
15824// dlsym() must pass its own return address to the loader so that
15825// RTLD_NEXT can identify the calling object.  Keep the public entry
15826// point in assembly and tail-jump into the Rust implementation.
15827#[no_mangle]
15828#[unsafe(naked)]
15829#[cfg(target_arch = "x86_64")]
15830pub unsafe extern "C" fn dlsym(_handle: *mut c_void, _symbol: *const c_char) -> *mut c_void {
15831    core::arch::naked_asm!("mov rdx, [rsp]", "jmp dlsym_impl");
15832}
15833
15834#[no_mangle]
15835#[unsafe(naked)]
15836#[cfg(target_arch = "aarch64")]
15837pub unsafe extern "C" fn dlsym(_handle: *mut c_void, _symbol: *const c_char) -> *mut c_void {
15838    core::arch::naked_asm!("mov x2, x30", "b dlsym_impl");
15839}
15840
15841#[no_mangle]
15842#[unsafe(naked)]
15843#[cfg(target_arch = "riscv64")]
15844pub unsafe extern "C" fn dlsym(_handle: *mut c_void, _symbol: *const c_char) -> *mut c_void {
15845    core::arch::naked_asm!("mv a2, ra", "la t0, dlsym_impl", "jr t0");
15846}
15847
15848#[no_mangle]
15849pub unsafe extern "C" fn dlclose(handle: *mut c_void) -> c_int {
15850    if let Some(f) = LDSO_DLCLOSE {
15851        f(handle)
15852    } else {
15853        0
15854    }
15855}
15856
15857#[no_mangle]
15858pub unsafe extern "C" fn dlerror() -> *const c_char {
15859    if let Some(f) = LDSO_DLERROR {
15860        f()
15861    } else {
15862        core::ptr::null()
15863    }
15864}
15865
15866include!("crypt_impl.rs");
15867include!("statvfs.rs");
15868include!("daemon.rs");
15869include!("dn_expand.rs");
15870include!("lrand48.rs");
15871include!("strverscmp.rs");
15872include!("syscall.rs");
15873include!("pthread_atfork.rs");
15874include!("fenv.rs");
15875include!("locale_ctype.rs");
15876include!("regression_stubs.rs");
15877include!("netdb_extra.rs");
15878include!("compat_extra.rs");
15879include!("stdio_extra.rs");
15880include!("ns_parse.rs");
15881include!("fts.rs");
15882include!("ifaddrs.rs");
15883include!("dl_extra.rs");
15884include!("des.rs");
15885include!("path_extra.rs");
15886include!("wordexp.rs");
15887include!("getopt_impl.rs");
15888
15889// ============================================================
15890// regex.h
15891// ============================================================
15892
15893type regoff_t = c_int;
15894
15895#[repr(C)]
15896pub struct regex_t {
15897    pub re_nsub: usize,
15898    pub __opaque: *mut c_void,
15899    pub __padding: [*mut c_void; 4],
15900    pub __nsub2: usize,
15901    pub __padding2: c_char,
15902}
15903
15904#[repr(C)]
15905pub struct regmatch_t {
15906    pub rm_so: regoff_t,
15907    pub rm_eo: regoff_t,
15908}
15909
15910include!("regex.rs");
15911
15912#[cfg(test)]
15913mod libc_unit_stub {
15914    #[test]
15915    fn stub() {
15916        assert!(true);
15917    }
15918}
15919
15920// ============================================================
15921// Missing math functions - simple implementations
15922// ============================================================
15923
15924#[inline]
15925pub(crate) fn fmax_impl(x: f64, y: f64) -> f64 {
15926    if x.is_nan() { return y; }
15927    if y.is_nan() { return x; }
15928    if x == 0.0 && y == 0.0 {
15929        return if x.is_sign_negative() && y.is_sign_negative() { -0.0 } else { 0.0 };
15930    }
15931    if x > y { x } else { y }
15932}
15933
15934#[inline]
15935pub(crate) fn fmin_impl(x: f64, y: f64) -> f64 {
15936    if x.is_nan() { return y; }
15937    if y.is_nan() { return x; }
15938    if x == 0.0 && y == 0.0 {
15939        return if x.is_sign_negative() || y.is_sign_negative() { -0.0 } else { 0.0 };
15940    }
15941    if x < y { x } else { y }
15942}
15943
15944#[inline]
15945pub(crate) fn fmaxf_impl(x: f32, y: f32) -> f32 {
15946    if x.is_nan() { return y; }
15947    if y.is_nan() { return x; }
15948    if x == 0.0 && y == 0.0 {
15949        return if x.is_sign_negative() && y.is_sign_negative() { -0.0 } else { 0.0 };
15950    }
15951    if x > y { x } else { y }
15952}
15953
15954#[inline]
15955pub(crate) fn fminf_impl(x: f32, y: f32) -> f32 {
15956    if x.is_nan() { return y; }
15957    if y.is_nan() { return x; }
15958    if x == 0.0 && y == 0.0 {
15959        return if x.is_sign_negative() || y.is_sign_negative() { -0.0 } else { 0.0 };
15960    }
15961    if x < y { x } else { y }
15962}
15963
15964#[inline(never)]
15965#[no_mangle]
15966pub unsafe extern "C" fn fmax(x: f64, y: f64) -> f64 {
15967    fmax_impl(x, y)
15968}
15969
15970#[inline(never)]
15971#[no_mangle]
15972pub unsafe extern "C" fn fmin(x: f64, y: f64) -> f64 {
15973    fmin_impl(x, y)
15974}
15975
15976#[no_mangle]
15977pub unsafe extern "C" fn fminf(x: f32, y: f32) -> f32 {
15978    fminf_impl(x, y)
15979}
15980
15981#[no_mangle]
15982pub unsafe extern "C" fn fdim(x: f64, y: f64) -> f64 {
15983    if x.is_nan() || y.is_nan() {
15984        return f64::NAN;
15985    }
15986    if x > y { x - y } else { 0.0 }
15987}
15988
15989#[no_mangle]
15990pub unsafe extern "C" fn fma(x: f64, y: f64, z: f64) -> f64 {
15991    if (x.is_infinite() && y == 0.0) || (y.is_infinite() && x == 0.0) {
15992        feraiseexcept(FE_INVALID);
15993        return f64::NAN;
15994    }
15995    if x.is_nan() || y.is_nan() || z.is_nan() {
15996        return f64::NAN;
15997    }
15998    let r = libm::fma(x, y, z);
15999    // inf-inf is invalid; libm may return NaN without raising.
16000    if r.is_nan() {
16001        feraiseexcept(FE_INVALID);
16002    }
16003    r
16004}
16005
16006#[no_mangle]
16007pub unsafe extern "C" fn ilogb(x: f64) -> i32 {
16008    if x.is_nan() {
16009        feraiseexcept(FE_INVALID);
16010        return -0x7fffffff - 1; // FP_ILOGBNAN
16011    }
16012    if x == 0.0 || x.is_infinite() {
16013        feraiseexcept(FE_INVALID);
16014        return if x == 0.0 { -0x7fffffff - 1 } else { 0x7fffffff };
16015    }
16016    let bits = x.to_bits();
16017    let exp = ((bits >> 52) & 0x7ff) as i32;
16018    if exp == 0 {
16019        // subnormal
16020        let mut v = bits & 0xfffffffffffff;
16021        let mut e = -1022;
16022        while v < (1u64 << 52) { v <<= 1; e -= 1; }
16023        e
16024    } else {
16025        exp - 1023
16026    }
16027}
16028
16029#[no_mangle]
16030pub unsafe extern "C" fn logb(x: f64) -> f64 {
16031    if x.is_nan() { return x; }
16032    if x == 0.0 { return __math_divzero(1); }
16033    let bits = x.to_bits();
16034    let exp = ((bits >> 52) & 0x7ff) as i32;
16035    if exp == 0 {
16036        // subnormal
16037        let mut v = bits & 0xfffffffffffff;
16038        let mut e = -1022i32;
16039        while v < (1u64 << 52) { v <<= 1; e -= 1; }
16040        e as f64
16041    } else if exp == 0x7ff {
16042        x * x // infinity * infinity = infinity (preserves sign)
16043    } else {
16044        (exp - 1023) as f64
16045    }
16046}
16047
16048#[no_mangle]
16049pub unsafe extern "C" fn nearbyint(x: f64) -> f64 {
16050    let had_inexact = fetestexcept(FE_INEXACT) & FE_INEXACT != 0;
16051    let r = libm::rint(x);
16052    if !had_inexact {
16053        feclearexcept(FE_INEXACT);
16054    }
16055    r
16056}
16057
16058#[no_mangle]
16059pub unsafe extern "C" fn nextafter(x: f64, y: f64) -> f64 {
16060    if x.is_nan() || y.is_nan() { return x + y; }
16061    if x == 0.0 && y == 0.0 { return y; }
16062    if x == y { return x; }
16063    let r = libm::nextafter(x, y);
16064    if r.is_infinite() && x.is_finite() {
16065        feraiseexcept(FE_OVERFLOW | FE_INEXACT);
16066    } else if r != x {
16067        let bits = r.to_bits();
16068        let is_subnormal = (bits & 0x7ff0000000000000) == 0 && (bits & 0xfffffffffffff) != 0;
16069        if r == 0.0 || is_subnormal {
16070            feraiseexcept(FE_UNDERFLOW | FE_INEXACT);
16071        }
16072    }
16073    r
16074}
16075
16076#[no_mangle]
16077pub unsafe extern "C" fn nexttoward(x: f64, y: f64) -> f64 {
16078    // On x86_64, long double = f64, so nexttoward = nextafter
16079    if x == 0.0 && y == 0.0 {
16080        return if y.is_sign_negative() { -0.0 } else { 0.0 };
16081    }
16082    nextafter(x, y)
16083}
16084
16085#[no_mangle]
16086pub unsafe extern "C" fn remainder(x: f64, y: f64) -> f64 {
16087    if x.is_nan() || y.is_nan() { return x + y; }
16088    if y == 0.0 || x.is_infinite() { return __math_invalid(x); }
16089    libm::remainder(x, y)
16090}
16091
16092#[no_mangle]
16093pub unsafe extern "C" fn remquo(x: f64, y: f64, quo: *mut i32) -> f64 {
16094    if x.is_nan() || y.is_nan() {
16095        if !quo.is_null() { *quo = 0; }
16096        return x + y;
16097    }
16098    if y == 0.0 || x.is_infinite() {
16099        if !quo.is_null() { *quo = 0; }
16100        return __math_invalid(x);
16101    }
16102    let (r, q) = libm::remquo(x, y);
16103    if !quo.is_null() { *quo = q; }
16104    r
16105}
16106
16107#[no_mangle]
16108pub unsafe extern "C" fn exp2(x: f64) -> f64 {
16109    exp2_impl(x)
16110}
16111
16112#[no_mangle]
16113pub unsafe extern "C" fn expm1(x: f64) -> f64 {
16114    let r = libm::expm1(x);
16115    // Raise UNDERFLOW for very small results that underflow
16116    if r != 0.0 && x != 0.0 && libm::fabs(x) < 1e-307 {
16117        feraiseexcept(FE_UNDERFLOW | FE_INEXACT);
16118    }
16119    r
16120}
16121
16122#[no_mangle]
16123pub unsafe extern "C" fn log1p(x: f64) -> f64 {
16124    if x == -1.0 {
16125        feraiseexcept(FE_DIVBYZERO);
16126        return f64::NEG_INFINITY;
16127    }
16128    libm::log1p(x)
16129}
16130
16131#[no_mangle]
16132pub unsafe extern "C" fn acosh(x: f64) -> f64 {
16133    if x < 1.0 {
16134        feraiseexcept(FE_INVALID);
16135        return f64::NAN;
16136    }
16137    libm::acosh(x)
16138}
16139
16140#[no_mangle]
16141pub unsafe extern "C" fn asinh(x: f64) -> f64 {
16142    if x == 0.0 { return x; }
16143    libm::asinh(x)
16144}
16145
16146#[no_mangle]
16147pub unsafe extern "C" fn atanh(x: f64) -> f64 {
16148    if x == 0.0 { return x; }
16149    let e = ((x.to_bits() >> 52) & 0x7ff) as i32;
16150    if e < 0x3ff - 32 {
16151        feraiseexcept(FE_INEXACT);
16152        if e == 0 { feraiseexcept(FE_UNDERFLOW); }
16153        return x;
16154    }
16155    if x == 1.0 {
16156        feraiseexcept(FE_DIVBYZERO);
16157        return f64::INFINITY;
16158    }
16159    if x == -1.0 {
16160        feraiseexcept(FE_DIVBYZERO);
16161        return f64::NEG_INFINITY;
16162    }
16163    if x.is_nan() { return x; }
16164    let ax = libm::fabs(x);
16165    if ax > 1.0 {
16166        feraiseexcept(FE_INVALID);
16167        return f64::NAN;
16168    }
16169    if ax < 0.5 {
16170        // For small x: use log1p formula with |x| and negate
16171        let r = libm::log1p(2.0 * (ax + ax * ax / (1.0 - ax))) / 2.0;
16172        if x < 0.0 { -r } else { r }
16173    } else {
16174        // For larger x: use log formula with |x| and negate
16175        let r = 0.5 * libm::log((1.0 + ax) / (1.0 - ax));
16176        if x < 0.0 { -r } else { r }
16177    }
16178}
16179
16180#[no_mangle]
16181pub unsafe extern "C" fn cbrt(x: f64) -> f64 {
16182    libm::cbrt(x)
16183}
16184
16185#[no_mangle]
16186pub unsafe extern "C" fn erf(x: f64) -> f64 {
16187    erf_impl(x)
16188}
16189
16190#[no_mangle]
16191pub unsafe extern "C" fn erfc(x: f64) -> f64 {
16192    erfc_impl(x)
16193}
16194
16195#[no_mangle]
16196pub unsafe extern "C" fn lgamma(x: f64) -> f64 {
16197    lgamma_impl(x)
16198}
16199
16200#[no_mangle]
16201pub unsafe extern "C" fn tgamma(x: f64) -> f64 {
16202    tgamma_impl(x)
16203}
16204
16205#[no_mangle]
16206pub unsafe extern "C" fn lround(x: f64) -> core::ffi::c_long {
16207    libm::round(x) as core::ffi::c_long
16208}
16209
16210#[no_mangle]
16211pub unsafe extern "C" fn llround(x: f64) -> i64 {
16212    libm::round(x) as i64
16213}
16214
16215// ============================================================
16216// Complex math functions - using (f64, f64) pairs
16217// ============================================================
16218
16219// Helper: complex number as (real, imag) pair
16220// cabs: magnitude = sqrt(re^2 + im^2)
16221#[no_mangle]
16222pub unsafe extern "C" fn cabs(z: (f64, f64)) -> f64 {
16223    libm::sqrt(z.0 * z.0 + z.1 * z.1)
16224}
16225
16226#[no_mangle]
16227pub unsafe extern "C" fn cabsf(z: (f32, f32)) -> f32 {
16228    libm::sqrtf(z.0 * z.0 + z.1 * z.1)
16229}
16230
16231#[no_mangle]
16232pub unsafe extern "C" fn cabsl(z: (f64, f64)) -> f64 {
16233    cabs(z)
16234}
16235
16236#[no_mangle]
16237pub unsafe extern "C" fn carg(z: (f64, f64)) -> f64 {
16238    libm::atan2(z.1, z.0)
16239}
16240
16241#[no_mangle]
16242pub unsafe extern "C" fn cargf(z: (f32, f32)) -> f32 {
16243    libm::atan2f(z.1, z.0)
16244}
16245
16246#[no_mangle]
16247pub unsafe extern "C" fn cargl(z: (f64, f64)) -> f64 {
16248    carg(z)
16249}
16250
16251#[no_mangle]
16252pub unsafe extern "C" fn cimag(z: (f64, f64)) -> f64 {
16253    z.1
16254}
16255
16256#[no_mangle]
16257pub unsafe extern "C" fn cimagf(z: (f32, f32)) -> f32 {
16258    z.1
16259}
16260
16261#[no_mangle]
16262pub unsafe extern "C" fn cimagl(z: (f64, f64)) -> f64 {
16263    z.1
16264}
16265
16266#[no_mangle]
16267pub unsafe extern "C" fn creal(z: (f64, f64)) -> f64 {
16268    z.0
16269}
16270
16271#[no_mangle]
16272pub unsafe extern "C" fn crealf(z: (f32, f32)) -> f32 {
16273    z.0
16274}
16275
16276#[no_mangle]
16277pub unsafe extern "C" fn creall(z: (f64, f64)) -> f64 {
16278    z.0
16279}
16280
16281#[no_mangle]
16282pub unsafe extern "C" fn conj(z: (f64, f64)) -> (f64, f64) {
16283    (z.0, -z.1)
16284}
16285
16286#[no_mangle]
16287pub unsafe extern "C" fn conjf(z: (f32, f32)) -> (f32, f32) {
16288    (z.0, -z.1)
16289}
16290
16291#[no_mangle]
16292pub unsafe extern "C" fn conjl(z: (f64, f64)) -> (f64, f64) {
16293    conj(z)
16294}
16295
16296#[no_mangle]
16297pub unsafe extern "C" fn cproj(z: (f64, f64)) -> (f64, f64) {
16298    if z.0.is_infinite() || z.1.is_infinite() {
16299        (f64::INFINITY, if z.1.is_sign_positive() { 0.0 } else { -0.0 })
16300    } else {
16301        z
16302    }
16303}
16304
16305#[no_mangle]
16306pub unsafe extern "C" fn cprojf(z: (f32, f32)) -> (f32, f32) {
16307    if z.0.is_infinite() || z.1.is_infinite() {
16308        (f32::INFINITY, if z.1.is_sign_positive() { 0.0 } else { -0.0 })
16309    } else {
16310        z
16311    }
16312}
16313
16314#[no_mangle]
16315pub unsafe extern "C" fn cprojl(z: (f64, f64)) -> (f64, f64) {
16316    cproj(z)
16317}
16318
16319// cexp: e^z = e^(re + i*im) = e^re * (cos(im) + i*sin(im))
16320#[no_mangle]
16321pub unsafe extern "C" fn cexp(z: (f64, f64)) -> (f64, f64) {
16322    let r = libm::exp(z.0);
16323    (r * libm::cos(z.1), r * libm::sin(z.1))
16324}
16325
16326#[no_mangle]
16327pub unsafe extern "C" fn cexpf(z: (f32, f32)) -> (f32, f32) {
16328    let r = libm::expf(z.0);
16329    (r * libm::cosf(z.1), r * libm::sinf(z.1))
16330}
16331
16332#[no_mangle]
16333pub unsafe extern "C" fn cexpl(z: (f64, f64)) -> (f64, f64) {
16334    cexp(z)
16335}
16336
16337// clog: ln(z) = ln|z| + i*arg(z)
16338#[no_mangle]
16339pub unsafe extern "C" fn clog(z: (f64, f64)) -> (f64, f64) {
16340    (libm::log(cabs(z)), carg(z))
16341}
16342
16343#[no_mangle]
16344pub unsafe extern "C" fn clogf(z: (f32, f32)) -> (f32, f32) {
16345    (libm::logf(cabsf(z)), cargf(z))
16346}
16347
16348#[no_mangle]
16349pub unsafe extern "C" fn clogl(z: (f64, f64)) -> (f64, f64) {
16350    clog(z)
16351}
16352
16353// csqrt: sqrt(z) = sqrt((|z|+re)/2) + i*sign(im)*sqrt((|z|-re)/2)
16354#[no_mangle]
16355pub unsafe extern "C" fn csqrt(z: (f64, f64)) -> (f64, f64) {
16356    let r = cabs(z);
16357    let sign = if z.1 < 0.0 { -1.0 } else { 1.0 };
16358    (libm::sqrt((r + z.0) / 2.0), sign * libm::sqrt((r - z.0) / 2.0))
16359}
16360
16361#[no_mangle]
16362pub unsafe extern "C" fn csqrtf(z: (f32, f32)) -> (f32, f32) {
16363    let r = cabsf(z);
16364    let sign = if z.1 < 0.0 { -1.0f32 } else { 1.0f32 };
16365    (libm::sqrtf((r + z.0) / 2.0), sign * libm::sqrtf((r - z.0) / 2.0))
16366}
16367
16368#[no_mangle]
16369pub unsafe extern "C" fn csqrtl(z: (f64, f64)) -> (f64, f64) {
16370    csqrt(z)
16371}
16372
16373// cpow: z^w = e^(w * log(z))
16374#[no_mangle]
16375pub unsafe extern "C" fn cpow(z: (f64, f64), w: (f64, f64)) -> (f64, f64) {
16376    let log_z = clog(z);
16377    let exp_arg = (w.0 * log_z.0 - w.1 * log_z.1, w.0 * log_z.1 + w.1 * log_z.0);
16378    cexp(exp_arg)
16379}
16380
16381#[no_mangle]
16382pub unsafe extern "C" fn cpowf(z: (f32, f32), w: (f32, f32)) -> (f32, f32) {
16383    let log_z = clogf(z);
16384    let exp_arg = (w.0 * log_z.0 - w.1 * log_z.1, w.0 * log_z.1 + w.1 * log_z.0);
16385    cexpf(exp_arg)
16386}
16387
16388#[no_mangle]
16389pub unsafe extern "C" fn cpowl(z: (f64, f64), w: (f64, f64)) -> (f64, f64) {
16390    cpow(z, w)
16391}
16392
16393// ccos: cos(z) = cos(re)*cosh(im) - i*sin(re)*sinh(im)
16394#[no_mangle]
16395pub unsafe extern "C" fn ccos(z: (f64, f64)) -> (f64, f64) {
16396    (libm::cos(z.0) * libm::cosh(z.1), -libm::sin(z.0) * libm::sinh(z.1))
16397}
16398
16399#[no_mangle]
16400pub unsafe extern "C" fn ccosf(z: (f32, f32)) -> (f32, f32) {
16401    (libm::cosf(z.0) * libm::coshf(z.1), -libm::sinf(z.0) * libm::sinhf(z.1))
16402}
16403
16404#[no_mangle]
16405pub unsafe extern "C" fn ccosl(z: (f64, f64)) -> (f64, f64) {
16406    ccos(z)
16407}
16408
16409// csin: sin(z) = sin(re)*cosh(im) + i*cos(re)*sinh(im)
16410#[no_mangle]
16411pub unsafe extern "C" fn csin(z: (f64, f64)) -> (f64, f64) {
16412    (libm::sin(z.0) * libm::cosh(z.1), libm::cos(z.0) * libm::sinh(z.1))
16413}
16414
16415#[no_mangle]
16416pub unsafe extern "C" fn csinf(z: (f32, f32)) -> (f32, f32) {
16417    (libm::sinf(z.0) * libm::coshf(z.1), libm::cosf(z.0) * libm::sinhf(z.1))
16418}
16419
16420#[no_mangle]
16421pub unsafe extern "C" fn csinl(z: (f64, f64)) -> (f64, f64) {
16422    csin(z)
16423}
16424
16425// ctan: tan(z) = sin(2re)/(cos(2re)+cosh(2im)) + i*sinh(2im)/(cos(2re)+cosh(2im))
16426#[no_mangle]
16427pub unsafe extern "C" fn ctan(z: (f64, f64)) -> (f64, f64) {
16428    let d = libm::cos(2.0 * z.0) + libm::cosh(2.0 * z.1);
16429    (libm::sin(2.0 * z.0) / d, libm::sinh(2.0 * z.1) / d)
16430}
16431
16432#[no_mangle]
16433pub unsafe extern "C" fn ctanf(z: (f32, f32)) -> (f32, f32) {
16434    let d = libm::cosf(2.0 * z.0) + libm::coshf(2.0 * z.1);
16435    (libm::sinf(2.0 * z.0) / d, libm::sinhf(2.0 * z.1) / d)
16436}
16437
16438#[no_mangle]
16439pub unsafe extern "C" fn ctanl(z: (f64, f64)) -> (f64, f64) {
16440    ctan(z)
16441}
16442
16443// ccosh: cosh(z) = cosh(re)*cos(im) + i*sinh(re)*sin(im)
16444#[no_mangle]
16445pub unsafe extern "C" fn ccosh(z: (f64, f64)) -> (f64, f64) {
16446    (libm::cosh(z.0) * libm::cos(z.1), libm::sinh(z.0) * libm::sin(z.1))
16447}
16448
16449#[no_mangle]
16450pub unsafe extern "C" fn ccoshf(z: (f32, f32)) -> (f32, f32) {
16451    (libm::coshf(z.0) * libm::cosf(z.1), libm::sinhf(z.0) * libm::sinf(z.1))
16452}
16453
16454#[no_mangle]
16455pub unsafe extern "C" fn ccoshl(z: (f64, f64)) -> (f64, f64) {
16456    ccosh(z)
16457}
16458
16459// csinh: sinh(z) = sinh(re)*cos(im) + i*cosh(re)*sin(im)
16460#[no_mangle]
16461pub unsafe extern "C" fn csinh(z: (f64, f64)) -> (f64, f64) {
16462    (libm::sinh(z.0) * libm::cos(z.1), libm::cosh(z.0) * libm::sin(z.1))
16463}
16464
16465#[no_mangle]
16466pub unsafe extern "C" fn csinhf(z: (f32, f32)) -> (f32, f32) {
16467    (libm::sinhf(z.0) * libm::cosf(z.1), libm::coshf(z.0) * libm::sinf(z.1))
16468}
16469
16470#[no_mangle]
16471pub unsafe extern "C" fn csinhl(z: (f64, f64)) -> (f64, f64) {
16472    csinh(z)
16473}
16474
16475// ctanh: tanh(z) = sinh(2re)/(cosh(2re)+cos(2im)) + i*sin(2im)/(cosh(2re)+cos(2im))
16476#[no_mangle]
16477pub unsafe extern "C" fn ctanh(z: (f64, f64)) -> (f64, f64) {
16478    let d = libm::cosh(2.0 * z.0) + libm::cos(2.0 * z.1);
16479    (libm::sinh(2.0 * z.0) / d, libm::sin(2.0 * z.1) / d)
16480}
16481
16482#[no_mangle]
16483pub unsafe extern "C" fn ctanhf(z: (f32, f32)) -> (f32, f32) {
16484    let d = libm::coshf(2.0 * z.0) + libm::cosf(2.0 * z.1);
16485    (libm::sinhf(2.0 * z.0) / d, libm::sinf(2.0 * z.1) / d)
16486}
16487
16488#[no_mangle]
16489pub unsafe extern "C" fn ctanhl(z: (f64, f64)) -> (f64, f64) {
16490    ctanh(z)
16491}
16492
16493// cacos: acos(z) = pi/2 - asin(z)
16494// asin(z) = -i * log(i*z + sqrt(1 - z^2))
16495#[no_mangle]
16496pub unsafe extern "C" fn cacos(z: (f64, f64)) -> (f64, f64) {
16497    let asin_z = casin(z);
16498    (3.14159265358979323846264338327950288419716939937510582097494459230781640 / 2.0 - asin_z.0, -asin_z.1)
16499}
16500
16501#[no_mangle]
16502pub unsafe extern "C" fn cacosf(z: (f32, f32)) -> (f32, f32) {
16503    let asin_z = casinf(z);
16504    (3.14159265358979323846264338327950288419716939937510582097494459230781640f32 / 2.0 - asin_z.0, -asin_z.1)
16505}
16506
16507#[no_mangle]
16508pub unsafe extern "C" fn cacosl(z: (f64, f64)) -> (f64, f64) {
16509    cacos(z)
16510}
16511
16512// casin: asin(z) = -i * log(i*z + sqrt(1 - z^2))
16513#[no_mangle]
16514pub unsafe extern "C" fn casin(z: (f64, f64)) -> (f64, f64) {
16515    // i*z = (-z.1, z.0)
16516    let iz = (-z.1, z.0);
16517    // 1 - z^2 = 1 - (z.0^2 - z.1^2 + 2i*z.0*z.1) = (1 - z.0^2 + z.1^2, -2*z.0*z.1)
16518    let one_minus_z2 = (1.0 - z.0 * z.0 + z.1 * z.1, -2.0 * z.0 * z.1);
16519    let sqrt_val = csqrt(one_minus_z2);
16520    let arg = clog((iz.0 + sqrt_val.0, iz.1 + sqrt_val.1));
16521    // -i * log(...) = (arg.1, -arg.0)
16522    (arg.1, -arg.0)
16523}
16524
16525#[no_mangle]
16526pub unsafe extern "C" fn casinf(z: (f32, f32)) -> (f32, f32) {
16527    let iz = (-z.1, z.0);
16528    let one_minus_z2 = (1.0 - z.0 * z.0 + z.1 * z.1, -2.0 * z.0 * z.1);
16529    let sqrt_val = csqrtf(one_minus_z2);
16530    let arg = clogf((iz.0 + sqrt_val.0, iz.1 + sqrt_val.1));
16531    (arg.1, -arg.0)
16532}
16533
16534#[no_mangle]
16535pub unsafe extern "C" fn casinl(z: (f64, f64)) -> (f64, f64) {
16536    casin(z)
16537}
16538
16539// catan: atan(z) = (i/2) * log((1 - i*z)/(1 + i*z))
16540#[no_mangle]
16541pub unsafe extern "C" fn catan(z: (f64, f64)) -> (f64, f64) {
16542    // i*z = (-z.1, z.0)
16543    // 1 - i*z = (1 + z.1, -z.0)
16544    // 1 + i*z = (1 - z.1, z.0)
16545    let num = (1.0 + z.1, -z.0);
16546    let den = (1.0 - z.1, z.0);
16547    let ratio = cdiv(num, den);
16548    let log_ratio = clog(ratio);
16549    // (i/2) * log = (-log.1/2, log.0/2)
16550    (-log_ratio.1 / 2.0, log_ratio.0 / 2.0)
16551}
16552
16553#[no_mangle]
16554pub unsafe extern "C" fn catanf(z: (f32, f32)) -> (f32, f32) {
16555    let num = (1.0 + z.1, -z.0);
16556    let den = (1.0 - z.1, z.0);
16557    let ratio = cdivf(num, den);
16558    let log_ratio = clogf(ratio);
16559    (-log_ratio.1 / 2.0, log_ratio.0 / 2.0)
16560}
16561
16562#[no_mangle]
16563pub unsafe extern "C" fn catanl(z: (f64, f64)) -> (f64, f64) {
16564    catan(z)
16565}
16566
16567// Helper: complex division
16568fn cdiv(a: (f64, f64), b: (f64, f64)) -> (f64, f64) {
16569    let d = b.0 * b.0 + b.1 * b.1;
16570    ((a.0 * b.0 + a.1 * b.1) / d, (a.1 * b.0 - a.0 * b.1) / d)
16571}
16572
16573fn cdivf(a: (f32, f32), b: (f32, f32)) -> (f32, f32) {
16574    let d = b.0 * b.0 + b.1 * b.1;
16575    ((a.0 * b.0 + a.1 * b.1) / d, (a.1 * b.0 - a.0 * b.1) / d)
16576}
16577
16578// cacosh: acosh(z) = log(z + sqrt(z^2 - 1))
16579#[no_mangle]
16580pub unsafe extern "C" fn cacosh(z: (f64, f64)) -> (f64, f64) {
16581    let z2 = (z.0 * z.0 - z.1 * z.1, 2.0 * z.0 * z.1);
16582    let z2_minus_1 = (z2.0 - 1.0, z2.1);
16583    let sqrt_val = csqrt(z2_minus_1);
16584    clog((z.0 + sqrt_val.0, z.1 + sqrt_val.1))
16585}
16586
16587#[no_mangle]
16588pub unsafe extern "C" fn cacoshf(z: (f32, f32)) -> (f32, f32) {
16589    let z2 = (z.0 * z.0 - z.1 * z.1, 2.0 * z.0 * z.1);
16590    let z2_minus_1 = (z2.0 - 1.0, z2.1);
16591    let sqrt_val = csqrtf(z2_minus_1);
16592    clogf((z.0 + sqrt_val.0, z.1 + sqrt_val.1))
16593}
16594
16595#[no_mangle]
16596pub unsafe extern "C" fn cacoshl(z: (f64, f64)) -> (f64, f64) {
16597    cacosh(z)
16598}
16599
16600// casinh: asinh(z) = log(z + sqrt(z^2 + 1))
16601#[no_mangle]
16602pub unsafe extern "C" fn casinh(z: (f64, f64)) -> (f64, f64) {
16603    let z2 = (z.0 * z.0 - z.1 * z.1, 2.0 * z.0 * z.1);
16604    let z2_plus_1 = (z2.0 + 1.0, z2.1);
16605    let sqrt_val = csqrt(z2_plus_1);
16606    clog((z.0 + sqrt_val.0, z.1 + sqrt_val.1))
16607}
16608
16609#[no_mangle]
16610pub unsafe extern "C" fn casinhf(z: (f32, f32)) -> (f32, f32) {
16611    let z2 = (z.0 * z.0 - z.1 * z.1, 2.0 * z.0 * z.1);
16612    let z2_plus_1 = (z2.0 + 1.0, z2.1);
16613    let sqrt_val = csqrtf(z2_plus_1);
16614    clogf((z.0 + sqrt_val.0, z.1 + sqrt_val.1))
16615}
16616
16617#[no_mangle]
16618pub unsafe extern "C" fn casinhl(z: (f64, f64)) -> (f64, f64) {
16619    casinh(z)
16620}
16621
16622// catanh: atanh(z) = (1/2) * log((1+z)/(1-z))
16623#[no_mangle]
16624pub unsafe extern "C" fn catanh(z: (f64, f64)) -> (f64, f64) {
16625    let num = (1.0 + z.0, z.1);
16626    let den = (1.0 - z.0, -z.1);
16627    let ratio = cdiv(num, den);
16628    let log_ratio = clog(ratio);
16629    (log_ratio.0 / 2.0, log_ratio.1 / 2.0)
16630}
16631
16632#[no_mangle]
16633pub unsafe extern "C" fn catanhf(z: (f32, f32)) -> (f32, f32) {
16634    let num = (1.0 + z.0, z.1);
16635    let den = (1.0 - z.0, -z.1);
16636    let ratio = cdivf(num, den);
16637    let log_ratio = clogf(ratio);
16638    (log_ratio.0 / 2.0, log_ratio.1 / 2.0)
16639}
16640
16641#[no_mangle]
16642pub unsafe extern "C" fn catanhl(z: (f64, f64)) -> (f64, f64) {
16643    catanh(z)
16644}
16645
16646// ============================================================
16647// Float math functions (f32 versions)
16648// ============================================================
16649
16650#[inline(never)]
16651#[no_mangle]
16652pub unsafe extern "C" fn fmaxf(x: f32, y: f32) -> f32 {
16653    fmaxf_impl(x, y)
16654}
16655
16656#[no_mangle]
16657pub unsafe extern "C" fn fdimf(x: f32, y: f32) -> f32 {
16658    if x.is_nan() || y.is_nan() {
16659        return f32::NAN;
16660    }
16661    if x > y { x - y } else { 0.0 }
16662}
16663
16664#[no_mangle]
16665pub unsafe extern "C" fn fmaf(x: f32, y: f32, z: f32) -> f32 {
16666    if (x.is_infinite() && y == 0.0) || (y.is_infinite() && x == 0.0) {
16667        feraiseexcept(FE_INVALID);
16668        return f32::NAN;
16669    }
16670    if x.is_nan() || y.is_nan() || z.is_nan() {
16671        return f32::NAN;
16672    }
16673    let r = libm::fmaf(x, y, z);
16674    if r.is_nan() {
16675        feraiseexcept(FE_INVALID);
16676    }
16677    r
16678}
16679
16680#[no_mangle]
16681pub unsafe extern "C" fn ilogbf(x: f32) -> i32 {
16682    if x.is_nan() {
16683        feraiseexcept(FE_INVALID);
16684        return -0x7fffffff - 1;
16685    }
16686    if x == 0.0 || x.is_infinite() {
16687        feraiseexcept(FE_INVALID);
16688        return if x == 0.0 { -0x7fffffff - 1 } else { 0x7fffffff };
16689    }
16690    let bits = x.to_bits();
16691    let exp = ((bits >> 23) & 0xff) as i32;
16692    if exp == 0 {
16693        let mut v = bits & 0x7fffff;
16694        let mut e = -126;
16695        while v < (1u32 << 23) { v <<= 1; e -= 1; }
16696        e
16697    } else {
16698        exp - 127
16699    }
16700}
16701
16702#[no_mangle]
16703pub unsafe extern "C" fn logbf(x: f32) -> f32 {
16704    if x.is_nan() { return x; }
16705    if x == 0.0 { return __math_divzerof(1); }
16706    let bits = x.to_bits();
16707    let exp = ((bits >> 23) & 0xff) as i32;
16708    if exp == 0 {
16709        let mut v = bits & 0x7fffff;
16710        let mut e = -126i32;
16711        while v < (1u32 << 23) { v <<= 1; e -= 1; }
16712        e as f32
16713    } else if exp == 0xff {
16714        x * x
16715    } else {
16716        (exp - 127) as f32
16717    }
16718}
16719
16720#[no_mangle]
16721pub unsafe extern "C" fn nearbyintf(x: f32) -> f32 {
16722    let had_inexact = fetestexcept(FE_INEXACT) & FE_INEXACT != 0;
16723    let r = libm::rintf(x);
16724    if !had_inexact {
16725        feclearexcept(FE_INEXACT);
16726    }
16727    r
16728}
16729
16730#[no_mangle]
16731pub unsafe extern "C" fn nextafterf(x: f32, y: f32) -> f32 {
16732    if x.is_nan() || y.is_nan() { return x + y; }
16733    if x == 0.0 && y == 0.0 { return y; }
16734    if x == y { return x; }
16735    let r = libm::nextafterf(x, y);
16736    if r.is_infinite() && x.is_finite() {
16737        feraiseexcept(FE_OVERFLOW | FE_INEXACT);
16738    } else if r != x {
16739        let bits = r.to_bits();
16740        let is_subnormal = (bits & 0x7f800000) == 0 && (bits & 0x7fffff) != 0;
16741        if r == 0.0 || is_subnormal {
16742            feraiseexcept(FE_UNDERFLOW | FE_INEXACT);
16743        }
16744    }
16745    r
16746}
16747
16748#[no_mangle]
16749#[cfg(target_arch = "x86_64")]
16750pub unsafe extern "C" fn nexttowardf(x: f32, y: f64) -> f32 {
16751    if y.is_nan() { return (x as f64 + y) as f32; }
16752    if x == 0.0 && y == 0.0 {
16753        return if y.is_sign_negative() { -0.0 } else { 0.0 };
16754    }
16755    if (x as f64) == y { return x; }
16756    nextafterf(x, if y > x as f64 { f32::INFINITY } else { f32::NEG_INFINITY })
16757}
16758
16759#[no_mangle]
16760#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
16761pub unsafe extern "C" fn nexttowardf(x: f32, y: f128) -> f32 {
16762    if y.is_nan() { return (x as f128 + y) as f32; }
16763    if (x as f128) == y { return x; }
16764    nextafterf(x, if y > x as f128 { f32::INFINITY } else { f32::NEG_INFINITY })
16765}
16766
16767#[no_mangle]
16768pub unsafe extern "C" fn remainderf(x: f32, y: f32) -> f32 {
16769    if x.is_nan() || y.is_nan() { return x + y; }
16770    if y == 0.0 || x.is_infinite() { return __math_invalidf(x); }
16771    libm::remainderf(x, y)
16772}
16773
16774#[no_mangle]
16775pub unsafe extern "C" fn remquof(x: f32, y: f32, quo: *mut i32) -> f32 {
16776    if x.is_nan() || y.is_nan() {
16777        if !quo.is_null() { *quo = 0; }
16778        return x + y;
16779    }
16780    if y == 0.0 || x.is_infinite() {
16781        if !quo.is_null() { *quo = 0; }
16782        return __math_invalidf(x);
16783    }
16784    let (r, q) = libm::remquof(x, y);
16785    if !quo.is_null() { *quo = q; }
16786    r
16787}
16788
16789#[no_mangle]
16790pub unsafe extern "C" fn exp2f(x: f32) -> f32 {
16791    if x == f32::INFINITY { return f32::INFINITY; }
16792    if x == f32::NEG_INFINITY { return 0.0; }
16793    if x.is_nan() { return f32::NAN; }
16794    let r = libm::exp2f(x);
16795    if r.is_infinite() && x.is_finite() {
16796        feraiseexcept(FE_OVERFLOW | FE_INEXACT);
16797    } else if r == 0.0 && x < -149.0 {
16798        feraiseexcept(FE_UNDERFLOW | FE_INEXACT);
16799    } else if r != 0.0 && x != libm::truncf(x) {
16800        let bits = r.to_bits();
16801        if (bits & 0x7f800000) == 0 {
16802            feraiseexcept(FE_UNDERFLOW | FE_INEXACT);
16803        }
16804    }
16805    r
16806}
16807
16808#[no_mangle]
16809pub unsafe extern "C" fn expm1f(x: f32) -> f32 {
16810    libm::expm1f(x)
16811}
16812
16813#[no_mangle]
16814pub unsafe extern "C" fn log1pf(x: f32) -> f32 {
16815    if x == -1.0 {
16816        feraiseexcept(FE_DIVBYZERO);
16817        return f32::NEG_INFINITY;
16818    }
16819    libm::log1pf(x)
16820}
16821
16822#[no_mangle]
16823pub unsafe extern "C" fn acoshf(x: f32) -> f32 {
16824    if x < 1.0 {
16825        feraiseexcept(FE_INVALID);
16826        return f32::NAN;
16827    }
16828    libm::acoshf(x)
16829}
16830
16831#[no_mangle]
16832pub unsafe extern "C" fn asinhf(x: f32) -> f32 {
16833    if x == 0.0 { return x; }
16834    libm::asinhf(x)
16835}
16836
16837#[no_mangle]
16838pub unsafe extern "C" fn atanhf(x: f32) -> f32 {
16839    if x == 0.0 { return x; }
16840    let e = ((x.to_bits() >> 23) & 0xff) as i32;
16841    if e < 0x7f - 32 {
16842        feraiseexcept(FE_INEXACT);
16843        if e == 0 { feraiseexcept(FE_UNDERFLOW); }
16844        return x;
16845    }
16846    if x == 1.0 {
16847        feraiseexcept(FE_DIVBYZERO);
16848        return f32::INFINITY;
16849    }
16850    if x == -1.0 {
16851        feraiseexcept(FE_DIVBYZERO);
16852        return f32::NEG_INFINITY;
16853    }
16854    if x.is_nan() { return x; }
16855    let ax = libm::fabsf(x);
16856    if ax > 1.0 {
16857        feraiseexcept(FE_INVALID);
16858        return f32::NAN;
16859    }
16860    if ax < 0.5 {
16861        let r = libm::log1pf(2.0 * (ax + ax * ax / (1.0 - ax))) / 2.0;
16862        if x < 0.0 { -r } else { r }
16863    } else {
16864        let r = 0.5 * libm::logf((1.0 + ax) / (1.0 - ax));
16865        if x < 0.0 { -r } else { r }
16866    }
16867}
16868
16869#[no_mangle]
16870pub unsafe extern "C" fn cbrtf(x: f32) -> f32 {
16871    libm::cbrtf(x)
16872}
16873
16874#[no_mangle]
16875pub unsafe extern "C" fn erff(x: f32) -> f32 {
16876    libm::erff(x)
16877}
16878
16879#[no_mangle]
16880pub unsafe extern "C" fn erfcf(x: f32) -> f32 {
16881    libm::erfcf(x)
16882}
16883
16884#[no_mangle]
16885pub unsafe extern "C" fn lgammaf(x: f32) -> f32 {
16886    if x.is_nan() {
16887        crate::signgam = 1;
16888        return x;
16889    }
16890    if x == 0.0 {
16891        crate::signgam = if x.is_sign_negative() { -1 } else { 1 };
16892        feraiseexcept(FE_DIVBYZERO);
16893        return f32::INFINITY;
16894    }
16895    let (r, sign) = libm::lgammaf_r(x);
16896    crate::signgam = sign;
16897    r
16898}
16899
16900#[no_mangle]
16901pub unsafe extern "C" fn tgammaf(x: f32) -> f32 {
16902    if x.is_nan() { return x; }
16903    if x == 0.0 {
16904        feraiseexcept(FE_DIVBYZERO);
16905        return if x.is_sign_negative() { f32::NEG_INFINITY } else { f32::INFINITY };
16906    }
16907    if x.is_infinite() {
16908        return if x.is_sign_negative() { __math_invalidf(x) } else { f32::INFINITY };
16909    }
16910    if x < 0.0 && x == libm::floorf(x) {
16911        feraiseexcept(FE_INVALID);
16912        return f32::NAN;
16913    }
16914    libm::tgammaf(x)
16915}
16916
16917#[no_mangle]
16918pub unsafe extern "C" fn lroundf(x: f32) -> core::ffi::c_long {
16919    libm::roundf(x) as core::ffi::c_long
16920}
16921
16922#[no_mangle]
16923pub unsafe extern "C" fn llroundf(x: f32) -> i64 {
16924    libm::roundf(x) as i64
16925}
16926
16927// ============================================================
16928// Wide char functions
16929// ============================================================
16930
16931#[no_mangle]
16932pub unsafe extern "C" fn wcscasecmp(s1: *const u32, s2: *const u32) -> i32 {
16933    let mut i = 0;
16934    loop {
16935        let c1 = *s1.add(i);
16936        let c2 = *s2.add(i);
16937        if c1 == 0 || c2 == 0 { return (c1 as i32) - (c2 as i32); }
16938        let l1 = if c1 >= 'A' as u32 && c1 <= 'Z' as u32 { c1 + 32 } else { c1 };
16939        let l2 = if c2 >= 'A' as u32 && c2 <= 'Z' as u32 { c2 + 32 } else { c2 };
16940        if l1 != l2 { return (l1 as i32) - (l2 as i32); }
16941        i += 1;
16942    }
16943}
16944
16945#[no_mangle]
16946pub unsafe extern "C" fn wcscasecmp_l(s1: *const u32, s2: *const u32, _loc: *mut core::ffi::c_void) -> i32 {
16947    wcscasecmp(s1, s2)
16948}
16949
16950#[no_mangle]
16951pub unsafe extern "C" fn wcsncasecmp(s1: *const u32, s2: *const u32, n: usize) -> i32 {
16952    let mut i = 0;
16953    while i < n {
16954        let c1 = *s1.add(i);
16955        let c2 = *s2.add(i);
16956        if c1 == 0 || c2 == 0 { return (c1 as i32) - (c2 as i32); }
16957        let l1 = if c1 >= 'A' as u32 && c1 <= 'Z' as u32 { c1 + 32 } else { c1 };
16958        let l2 = if c2 >= 'A' as u32 && c2 <= 'Z' as u32 { c2 + 32 } else { c2 };
16959        if l1 != l2 { return (l1 as i32) - (l2 as i32); }
16960        i += 1;
16961    }
16962    0
16963}
16964
16965#[no_mangle]
16966pub unsafe extern "C" fn wcsncasecmp_l(s1: *const u32, s2: *const u32, n: usize, _loc: *mut core::ffi::c_void) -> i32 {
16967    wcsncasecmp(s1, s2, n)
16968}
16969
16970#[no_mangle]
16971pub unsafe extern "C" fn wcscoll(s1: *const u32, s2: *const u32) -> i32 {
16972    // ponytail: simplified collation - just compare codepoints
16973    let mut i = 0;
16974    loop {
16975        let c1 = *s1.add(i);
16976        let c2 = *s2.add(i);
16977        if c1 == 0 || c2 == 0 { return (c1 as i32) - (c2 as i32); }
16978        if c1 != c2 { return (c1 as i32) - (c2 as i32); }
16979        i += 1;
16980    }
16981}
16982
16983#[no_mangle]
16984pub unsafe extern "C" fn wcstok(s: *mut u32, sep: *const u32, p: *mut *mut u32) -> *mut u32 {
16985    let mut src = if s.is_null() { *p } else { s };
16986    // Skip leading separators
16987    while *src != 0 && *sep.add(0) == *src { src = src.add(1); }
16988    if *src == 0 { return core::ptr::null_mut(); }
16989    let start = src;
16990    // Find end of token
16991    while *src != 0 && *sep.add(0) != *src { src = src.add(1); }
16992    if *src != 0 {
16993        *src = 0;
16994        *p = src.add(1);
16995    } else {
16996        *p = src;
16997    }
16998    start
16999}
17000
17001#[no_mangle]
17002pub unsafe extern "C" fn wcswcs(haystack: *const u32, needle: *const u32) -> *mut u32 {
17003    if *needle == 0 { return haystack as *mut u32; }
17004    let mut i = 0;
17005    while *haystack.add(i) != 0 {
17006        let mut j = 0;
17007        while *needle.add(j) != 0 && *haystack.add(i + j) == *needle.add(j) {
17008            j += 1;
17009        }
17010        if *needle.add(j) == 0 { return haystack.add(i) as *mut u32; }
17011        i += 1;
17012    }
17013    core::ptr::null_mut()
17014}
17015
17016#[no_mangle]
17017pub unsafe extern "C" fn wcswidth(s: *const u32, n: usize) -> i32 {
17018    let mut width = 0i32;
17019    let mut i = 0;
17020    while i < n {
17021        let c = *s.add(i);
17022        if c == 0 { break; }
17023        // ponytail: simplified width - assume all printable chars are width 1
17024        width += 1;
17025        i += 1;
17026    }
17027    width
17028}
17029
17030#[no_mangle]
17031pub unsafe extern "C" fn wcsftime(s: *mut u32, maxsize: usize, fmt: *const u32, tm: *const c_void) -> usize {
17032    if s.is_null() || fmt.is_null() || maxsize == 0 { return 0; }
17033    // Convert the wide format string to multibyte, let strftime do the work,
17034    // then convert the result back.
17035    let mut cfmt = [0u8; 1024];
17036    let mut o = 0usize;
17037    let mut i = 0usize;
17038    let mut st: c_uint = 0;
17039    while *fmt.add(i) != 0 && o + 8 < cfmt.len() {
17040        let n = wcrtomb(
17041            cfmt.as_mut_ptr().add(o) as *mut c_char,
17042            *fmt.add(i) as c_int,
17043            &mut st as *mut c_uint,
17044        );
17045        if n == !0usize { return 0; }
17046        o += n;
17047        i += 1;
17048    }
17049    cfmt[o] = 0;
17050    let mut out = [0u8; 4096];
17051    let mut lim = maxsize.saturating_mul(4);
17052    if lim > out.len() { lim = out.len(); }
17053    let n = strftime(out.as_mut_ptr() as *mut c_char, lim, cfmt.as_ptr() as *const c_char, tm as *const tm);
17054    if n == 0 { return 0; }
17055    let mut ws = 0usize;
17056    let mut p = 0usize;
17057    while p < n && ws + 1 < maxsize {
17058        let mut wc: c_int = 0;
17059        let mut st2: c_uint = 0;
17060        let r = mbrtowc(&mut wc, out.as_ptr().add(p) as *const c_char, n - p, &mut st2);
17061        if r == !0usize { return 0; }
17062        if r == 0 || r == !1usize { break; }
17063        *s.add(ws) = wc as u32;
17064        ws += 1;
17065        p += r;
17066    }
17067    *s.add(ws) = 0;
17068    ws
17069}
17070
17071#[no_mangle]
17072pub unsafe extern "C" fn mbsnrtowcs(
17073    dst: *mut u32,
17074    src: *mut *const u8,
17075    nms: usize,
17076    len: usize,
17077    st: *mut core::ffi::c_void,
17078) -> usize {
17079    if src.is_null() { return 0; }
17080    let mut s = *src;
17081    if s.is_null() { return 0; }
17082    let state = st as *mut c_uint;
17083    let limit = if dst.is_null() { usize::MAX } else { len };
17084    let mut count = 0usize;
17085    let mut n = nms;
17086    while count < limit && n > 0 {
17087        let c = *s;
17088        if c == 0 {
17089            if !dst.is_null() {
17090                if count < len { *dst.add(count) = 0; }
17091                *src = core::ptr::null();
17092            }
17093            return count;
17094        }
17095        let mut wc: c_int = 0;
17096        let r = mbrtowc(&mut wc, s as *const c_char, n, state);
17097        if r == !0usize {
17098            *src = s;
17099            return !0usize;
17100        }
17101        if r == !1usize { break; }
17102        if !dst.is_null() { *dst.add(count) = wc as u32; }
17103        count += 1;
17104        s = s.add(r);
17105        n -= r;
17106    }
17107    *src = s;
17108    count
17109}
17110
17111#[no_mangle]
17112pub unsafe extern "C" fn wcsnrtombs(dst: *mut u8, src: *mut *const u32, nwc: usize, len: usize, _st: *mut core::ffi::c_void) -> usize {
17113    let mut count = 0;
17114    let mut s = *src;
17115    let mut d = dst;
17116    while count < len && count < nwc {
17117        let c = *s;
17118        if c == 0 { break; }
17119        if !d.is_null() {
17120            if c < 0x80 {
17121                *d = c as u8;
17122                d = d.add(1);
17123                count += 1;
17124            } else if c < 0x800 {
17125                if count + 2 > len { break; }
17126                *d = 0xc0 | (c >> 6) as u8;
17127                *d.add(1) = 0x80 | (c & 0x3f) as u8;
17128                d = d.add(2);
17129                count += 2;
17130            } else if c < 0x10000 {
17131                if count + 3 > len { break; }
17132                *d = 0xe0 | (c >> 12) as u8;
17133                *d.add(1) = 0x80 | ((c >> 6) & 0x3f) as u8;
17134                *d.add(2) = 0x80 | (c & 0x3f) as u8;
17135                d = d.add(3);
17136                count += 3;
17137            } else {
17138                if count + 4 > len { break; }
17139                *d = 0xf0 | (c >> 18) as u8;
17140                *d.add(1) = 0x80 | ((c >> 12) & 0x3f) as u8;
17141                *d.add(2) = 0x80 | ((c >> 6) & 0x3f) as u8;
17142                *d.add(3) = 0x80 | (c & 0x3f) as u8;
17143                d = d.add(4);
17144                count += 4;
17145            }
17146        }
17147        s = s.add(1);
17148    }
17149    if !dst.is_null() { *src = s; }
17150    count
17151}
17152
17153// ============================================================
17154// Thread sync functions (C11 threads)
17155// ============================================================
17156
17157
17158// C11 thread types
17159pub type thrd_start_t = Option<unsafe extern "C" fn(arg: *mut core::ffi::c_void) -> i32>;
17160
17161// Thread function stubs - these need actual implementation
17162#[no_mangle]
17163pub unsafe extern "C" fn thrd_create(thr: *mut usize, func: thrd_start_t, arg: *mut core::ffi::c_void) -> i32 {
17164    let mut thread: u64 = 0;
17165    let r = pthread_create(&mut thread, core::ptr::null(), core::mem::transmute(func), arg);
17166    if r == 0 {
17167        *thr = thread as usize;
17168        0 // thrd_success
17169    } else {
17170        -1 // thrd_nomem
17171    }
17172}
17173
17174#[no_mangle]
17175pub unsafe extern "C" fn thrd_join(thr: usize, res: *mut i32) -> i32 {
17176    let mut retval: *mut core::ffi::c_void = core::ptr::null_mut();
17177    let r = pthread_join(thr as u64, &mut retval);
17178    if r == 0 {
17179        if !res.is_null() { *res = retval as i32; }
17180        0
17181    } else {
17182        -1
17183    }
17184}
17185
17186#[no_mangle]
17187pub unsafe extern "C" fn thrd_exit(res: i32) {
17188    pthread_exit(res as *mut core::ffi::c_void);
17189}
17190
17191#[no_mangle]
17192pub unsafe extern "C" fn thrd_detach(thr: usize) -> i32 {
17193    let r = pthread_detach(thr as u64);
17194    if r == 0 { 0 } else { -1 }
17195}
17196
17197#[no_mangle]
17198pub unsafe extern "C" fn thrd_current() -> usize {
17199    pthread_self() as usize
17200}
17201
17202#[no_mangle]
17203pub unsafe extern "C" fn thrd_equal(thr1: usize, thr2: usize) -> i32 {
17204    (thr1 == thr2) as i32
17205}
17206
17207#[no_mangle]
17208pub unsafe extern "C" fn thrd_sleep(duration: *const core::ffi::c_void, remaining: *mut core::ffi::c_void) -> i32 {
17209    nanosleep(duration as *const timespec, remaining as *mut timespec)
17210}
17211
17212#[no_mangle]
17213pub unsafe extern "C" fn thrd_yield() {
17214    sched_yield();
17215}
17216
17217// Mutex
17218#[no_mangle]
17219pub unsafe extern "C" fn mtx_init(mtx: *mut usize, _mtx_type: i32) -> i32 {
17220    let r = pthread_mutex_init(mtx as *mut pthread_mutex_t, core::ptr::null());
17221    if r == 0 { 0 } else { -1 }
17222}
17223
17224#[no_mangle]
17225pub unsafe extern "C" fn mtx_lock(mtx: *mut usize) -> i32 {
17226    let r = pthread_mutex_lock(mtx as *mut pthread_mutex_t);
17227    if r == 0 { 0 } else { -1 }
17228}
17229
17230#[no_mangle]
17231pub unsafe extern "C" fn mtx_trylock(mtx: *mut usize) -> i32 {
17232    let r = pthread_mutex_trylock(mtx as *mut pthread_mutex_t);
17233    if r == 0 { 0 } else { -1 }
17234}
17235
17236#[no_mangle]
17237pub unsafe extern "C" fn mtx_unlock(mtx: *mut usize) -> i32 {
17238    let r = pthread_mutex_unlock(mtx as *mut pthread_mutex_t);
17239    if r == 0 { 0 } else { -1 }
17240}
17241
17242#[no_mangle]
17243pub unsafe extern "C" fn mtx_destroy(mtx: *mut usize) {
17244    pthread_mutex_destroy(mtx as *mut pthread_mutex_t);
17245}
17246
17247#[no_mangle]
17248pub unsafe extern "C" fn mtx_timedlock(mtx: *mut usize, ts: *const core::ffi::c_void) -> i32 {
17249    let r = pthread_mutex_timedlock(mtx as *mut pthread_mutex_t, ts as *const timespec);
17250    if r == 0 { 0 } else { -1 }
17251}
17252
17253// Condition variable
17254#[no_mangle]
17255pub unsafe extern "C" fn cnd_init(cnd: *mut usize) -> i32 {
17256    let r = pthread_cond_init(cnd as *mut pthread_cond_t, core::ptr::null());
17257    if r == 0 { 0 } else { -1 }
17258}
17259
17260#[no_mangle]
17261pub unsafe extern "C" fn cnd_signal(cnd: *mut usize) -> i32 {
17262    let r = pthread_cond_signal(cnd as *mut pthread_cond_t);
17263    if r == 0 { 0 } else { -1 }
17264}
17265
17266#[no_mangle]
17267pub unsafe extern "C" fn cnd_broadcast(cnd: *mut usize) -> i32 {
17268    let r = pthread_cond_broadcast(cnd as *mut pthread_cond_t);
17269    if r == 0 { 0 } else { -1 }
17270}
17271
17272#[no_mangle]
17273pub unsafe extern "C" fn cnd_wait(cnd: *mut usize, mtx: *mut usize) -> i32 {
17274    let r = pthread_cond_wait(cnd as *mut pthread_cond_t, mtx as *mut pthread_mutex_t);
17275    if r == 0 { 0 } else { -1 }
17276}
17277
17278#[no_mangle]
17279pub unsafe extern "C" fn cnd_timedwait(cnd: *mut usize, mtx: *mut usize, ts: *const core::ffi::c_void) -> i32 {
17280    let r = pthread_cond_timedwait(cnd as *mut pthread_cond_t, mtx as *mut pthread_mutex_t, ts as *const timespec);
17281    if r == 0 { 0 } else { -1 }
17282}
17283
17284#[no_mangle]
17285pub unsafe extern "C" fn cnd_destroy(cnd: *mut usize) {
17286    pthread_cond_destroy(cnd as *mut pthread_cond_t);
17287}
17288
17289// Thread-specific storage
17290#[no_mangle]
17291pub unsafe extern "C" fn tss_create(key: *mut u32, dtor: Option<unsafe extern "C" fn(*mut core::ffi::c_void)>) -> i32 {
17292    let r = pthread_key_create(key, dtor);
17293    if r == 0 { 0 } else { -1 }
17294}
17295
17296#[no_mangle]
17297pub unsafe extern "C" fn tss_delete(key: u32) {
17298    pthread_key_delete(key);
17299}
17300
17301#[no_mangle]
17302pub unsafe extern "C" fn tss_set(key: u32, val: *mut core::ffi::c_void) -> i32 {
17303    let r = pthread_setspecific(key, val);
17304    if r == 0 { 0 } else { -1 }
17305}
17306
17307#[no_mangle]
17308pub unsafe extern "C" fn tss_get(key: u32) -> *mut core::ffi::c_void {
17309    pthread_getspecific(key)
17310}
17311
17312// call_once
17313#[no_mangle]
17314pub unsafe extern "C" fn call_once(flag: *mut u32, func: Option<unsafe extern "C" fn()>) {
17315    if *flag == 0 {
17316        if let Some(f) = func { f(); }
17317        *flag = 1;
17318    }
17319}
17320
17321// ============================================================
17322// Network functions
17323// ============================================================
17324
17325// accept4 - Linux-specific extension
17326#[no_mangle]
17327pub unsafe extern "C" fn accept4(fd: i32, addr: *mut core::ffi::c_void, addrlen: *mut u32, flags: i32) -> i32 {
17328    syscall_ret(<Arch as Syscalls>::syscall4(SYS_ACCEPT4, fd as i64, addr as i64, addrlen as i64, flags as i64)) as i32
17329}
17330
17331// getifaddrs / freeifaddrs
17332// ============================================================
17333// epoll/eventfd/signalfd/timerfd
17334// ============================================================
17335
17336#[no_mangle]
17337pub unsafe extern "C" fn epoll_create(size: i32) -> i32 {
17338    #[cfg(target_arch = "x86_64")]
17339    { syscall_ret(<Arch as Syscalls>::syscall1(SYS_EPOLL_CREATE, size as i64)) as i32 }
17340    #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
17341    {
17342        if size < 1 {
17343            ERRNO!() = EINVAL;
17344            return -1;
17345        }
17346        syscall_ret(<Arch as Syscalls>::syscall1(SYS_EPOLL_CREATE1, 0)) as i32
17347    }
17348}
17349
17350#[no_mangle]
17351pub unsafe extern "C" fn epoll_create1(flags: i32) -> i32 {
17352    syscall_ret(<Arch as Syscalls>::syscall1(SYS_EPOLL_CREATE1, flags as i64)) as i32
17353}
17354
17355#[no_mangle]
17356pub unsafe extern "C" fn epoll_ctl(epfd: i32, op: i32, fd: i32, event: *mut core::ffi::c_void) -> i32 {
17357    syscall_ret(<Arch as Syscalls>::syscall4(SYS_EPOLL_CTL, epfd as i64, op as i64, fd as i64, event as i64)) as i32
17358}
17359
17360#[no_mangle]
17361pub unsafe extern "C" fn epoll_wait(epfd: i32, events: *mut core::ffi::c_void, maxevents: i32, timeout: i32) -> i32 {
17362    #[cfg(target_arch = "x86_64")]
17363    {
17364        syscall_ret(<Arch as Syscalls>::syscall4(
17365            SYS_EPOLL_WAIT,
17366            epfd as i64,
17367            events as i64,
17368            maxevents as i64,
17369            timeout as i64,
17370        )) as i32
17371    }
17372    #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
17373    {
17374        epoll_pwait(epfd, events, maxevents, timeout, core::ptr::null())
17375    }
17376}
17377
17378#[no_mangle]
17379pub unsafe extern "C" fn epoll_pwait(
17380    epfd: i32,
17381    events: *mut core::ffi::c_void,
17382    maxevents: i32,
17383    timeout: i32,
17384    sigmask: *const u64,
17385) -> i32 {
17386    syscall_ret(<Arch as Syscalls>::syscall6(
17387        SYS_EPOLL_PWAIT,
17388        epfd as i64,
17389        events as i64,
17390        maxevents as i64,
17391        timeout as i64,
17392        sigmask as i64,
17393        8,
17394    )) as i32
17395}
17396
17397#[no_mangle]
17398pub unsafe extern "C" fn eventfd(initval: u32, flags: i32) -> i32 {
17399    syscall_ret(<Arch as Syscalls>::syscall2(SYS_EVENTFD2, initval as i64, flags as i64)) as i32
17400}
17401
17402#[no_mangle]
17403pub unsafe extern "C" fn eventfd_read(fd: i32, value: *mut u64) -> i32 {
17404    let n = crate::read(fd, value as *mut core::ffi::c_void, 8);
17405    if n == 8 { 0 } else { -1 }
17406}
17407
17408#[no_mangle]
17409pub unsafe extern "C" fn eventfd_write(fd: i32, value: u64) -> i32 {
17410    let n = crate::write(fd, &value as *const u64 as *const core::ffi::c_void, 8);
17411    if n == 8 { 0 } else { -1 }
17412}
17413
17414#[no_mangle]
17415pub unsafe extern "C" fn signalfd(fd: i32, mask: *const u64, flags: i32) -> i32 {
17416    syscall_ret(<Arch as Syscalls>::syscall4(SYS_SIGNALFD4, fd as i64, mask as i64, 8, flags as i64)) as i32
17417}
17418
17419#[no_mangle]
17420pub unsafe extern "C" fn timerfd_create(clockid: i32, flags: i32) -> i32 {
17421    syscall_ret(<Arch as Syscalls>::syscall2(SYS_TIMERFD_CREATE, clockid as i64, flags as i64)) as i32
17422}
17423
17424#[no_mangle]
17425pub unsafe extern "C" fn timerfd_settime(fd: i32, flags: i32, new_value: *const core::ffi::c_void, old_value: *mut core::ffi::c_void) -> i32 {
17426    syscall_ret(<Arch as Syscalls>::syscall4(SYS_TIMERFD_SETTIME, fd as i64, flags as i64, new_value as i64, old_value as i64)) as i32
17427}
17428
17429#[no_mangle]
17430pub unsafe extern "C" fn timerfd_gettime(fd: i32, curr_value: *mut core::ffi::c_void) -> i32 {
17431    syscall_ret(<Arch as Syscalls>::syscall2(SYS_TIMERFD_GETTIME, fd as i64, curr_value as i64)) as i32
17432}
17433
17434// ============================================================
17435// System functions
17436// ============================================================
17437
17438#[no_mangle]
17439pub unsafe extern "C" fn prctl(option: i32, mut args: ...) -> i32 {
17440    let a1 = args.next_arg::<i64>();
17441    let a2 = args.next_arg::<i64>();
17442    let a3 = args.next_arg::<i64>();
17443    let a4 = args.next_arg::<i64>();
17444    syscall_ret(<Arch as Syscalls>::syscall5(SYS_PRCTL, option as i64, a1, a2, a3, a4)) as i32
17445}
17446
17447#[no_mangle]
17448pub unsafe extern "C" fn quotactl(cmd: i32, special: *const core::ffi::c_char, id: i32, addr: *mut core::ffi::c_char) -> i32 {
17449    syscall_ret(<Arch as Syscalls>::syscall4(sysno(179, 60), cmd as i64, special as i64, id as i64, addr as i64)) as i32
17450}
17451
17452#[no_mangle]
17453pub unsafe extern "C" fn klogctl(type_: i32, bufp: *mut core::ffi::c_char, len: i32) -> i32 {
17454    syscall_ret(<Arch as Syscalls>::syscall3(sysno(103, 116), type_ as i64, bufp as i64, len as i64)) as i32
17455}
17456
17457static mut SYSLOG_IDENT: [core::ffi::c_char; 64] = [0; 64];
17458static mut SYSLOG_OPTIONS: i32 = 0;
17459static mut SYSLOG_FACILITY: i32 = 1;
17460
17461#[no_mangle]
17462pub unsafe extern "C" fn openlog(ident: *const core::ffi::c_char, option: i32, facility: i32) {
17463    SYSLOG_OPTIONS = option;
17464    SYSLOG_FACILITY = facility & 0x3f8;
17465    SYSLOG_IDENT[0] = 0;
17466    if !ident.is_null() {
17467        let mut i = 0;
17468        while i < 63 && *ident.add(i) != 0 {
17469            SYSLOG_IDENT[i] = *ident.add(i);
17470            i += 1;
17471        }
17472        SYSLOG_IDENT[i] = 0;
17473    }
17474}
17475
17476#[repr(C)]
17477struct SockAddrUn {
17478    sun_family: u16,
17479    sun_path: [core::ffi::c_char; 108],
17480}
17481
17482unsafe fn syslog_emit(priority: i32, msg: *const core::ffi::c_char, len: usize) {
17483    let mut out = [0u8; 1200];
17484    let mut o = 0usize;
17485    let pri = (SYSLOG_FACILITY | (priority & 7)) as u32;
17486    out[o] = b'<'; o += 1;
17487    if pri >= 100 { out[o] = b'0' + (pri / 100) as u8; o += 1; }
17488    if pri >= 10 { out[o] = b'0' + ((pri / 10) % 10) as u8; o += 1; }
17489    out[o] = b'0' + (pri % 10) as u8; o += 1;
17490    out[o] = b'>'; o += 1;
17491    let mut i = 0;
17492    while i < 63 && SYSLOG_IDENT[i] != 0 && o + 1 < out.len() {
17493        out[o] = SYSLOG_IDENT[i] as u8; o += 1; i += 1;
17494    }
17495    if SYSLOG_OPTIONS & 1 != 0 {
17496        out[o] = b'['; o += 1;
17497        let mut pid = crate::getpid() as u32;
17498        let mut digits = [0u8; 10];
17499        let mut d = 0;
17500        if pid == 0 { digits[0] = b'0'; d = 1; }
17501        while pid > 0 { digits[d] = b'0' + (pid % 10) as u8; pid /= 10; d += 1; }
17502        while d > 0 { d -= 1; out[o] = digits[d]; o += 1; }
17503        out[o] = b']'; o += 1;
17504    }
17505    out[o] = b':'; o += 1; out[o] = b' '; o += 1;
17506    let copy = core::cmp::min(len, out.len() - o - 1);
17507    core::ptr::copy_nonoverlapping(msg as *const u8, out.as_mut_ptr().add(o), copy);
17508    o += copy;
17509    let fd = crate::socket(AF_UNIX, SOCK_DGRAM, 0);
17510    if fd >= 0 {
17511        let mut sa: SockAddrUn = core::mem::zeroed();
17512        sa.sun_family = AF_UNIX as u16;
17513        let path = b"/dev/log";
17514        for i in 0..path.len() { sa.sun_path[i] = path[i] as core::ffi::c_char; }
17515        let r = crate::sendto(fd, out.as_ptr() as *const core::ffi::c_void, o, 0, &sa as *const SockAddrUn as *const crate::sockaddr, core::mem::size_of::<SockAddrUn>() as u32);
17516        crate::close(fd);
17517        if r >= 0 { return; }
17518    }
17519    crate::write(2, out.as_ptr() as *const core::ffi::c_void, o);
17520    crate::write(2, b"\n".as_ptr() as *const core::ffi::c_void, 1);
17521}
17522
17523#[no_mangle]
17524pub unsafe extern "C" fn vsyslog(priority: c_int, fmt: *const c_char, ap: VaList) {
17525    let mut msg = [0 as c_char; 1024];
17526    let n = vsnprintf(msg.as_mut_ptr(), msg.len(), fmt, ap);
17527    if n > 0 { syslog_emit(priority, msg.as_ptr(), core::cmp::min(n as usize, msg.len() - 1)); }
17528}
17529
17530#[no_mangle]
17531pub unsafe extern "C" fn syslog(priority: c_int, fmt: *const c_char, args: ...) {
17532    vsyslog(priority, fmt, args);
17533}
17534
17535#[no_mangle]
17536pub unsafe extern "C" fn closelog() {
17537    // Nothing to do
17538}
17539
17540#[no_mangle]
17541pub unsafe extern "C" fn setlogmask(mask: i32) -> i32 {
17542    static mut LOG_MASK: i32 = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80;
17543    let old = LOG_MASK;
17544    if mask != 0 { LOG_MASK = mask; }
17545    old
17546}
17547
17548#[no_mangle]
17549pub unsafe extern "C" fn acct(filename: *const core::ffi::c_char) -> i32 {
17550    syscall_ret(<Arch as Syscalls>::syscall1(SYS_ACCT, filename as i64)) as i32
17551}
17552
17553#[no_mangle]
17554pub unsafe extern "C" fn swapon(path: *const core::ffi::c_char, flags: i32) -> i32 {
17555    syscall_ret(<Arch as Syscalls>::syscall2(SYS_SWAPON, path as i64, flags as i64)) as i32
17556}
17557
17558#[no_mangle]
17559pub unsafe extern "C" fn swapoff(path: *const core::ffi::c_char) -> i32 {
17560    syscall_ret(<Arch as Syscalls>::syscall1(SYS_SWAPOFF, path as i64)) as i32
17561}
17562
17563#[no_mangle]
17564pub unsafe extern "C" fn syncfs(fd: i32) -> i32 {
17565    syscall_ret(<Arch as Syscalls>::syscall1(SYS_SYNCFS, fd as i64)) as i32
17566}
17567
17568#[no_mangle]
17569pub unsafe extern "C" fn sync_file_range(fd: i32, offset: i64, nbytes: u64, flags: u32) -> i32 {
17570    syscall_ret(<Arch as Syscalls>::syscall4(sysno(277, 84), fd as i64, offset, nbytes as i64, flags as i64)) as i32
17571}
17572
17573#[no_mangle]
17574pub unsafe extern "C" fn posix_fadvise(fd: i32, offset: i64, len: u64, advice: i32) -> i32 {
17575    // POSIX returns the error number directly, not -1/errno.
17576    let r = <Arch as Syscalls>::syscall4(sysno(221, 223), fd as i64, offset, len as i64, advice as i64);
17577    if r < 0 { (-r) as i32 } else { 0 }
17578}
17579
17580#[no_mangle]
17581pub unsafe extern "C" fn posix_fallocate(fd: i32, offset: i64, len: u64) -> i32 {
17582    let r = <Arch as Syscalls>::syscall3(SYS_FALLOCATE, fd as i64, offset, len as i64);
17583    if r < 0 { (-r) as i32 } else { 0 }
17584}
17585
17586#[no_mangle]
17587pub unsafe extern "C" fn readahead(fd: i32, offset: i64, count: usize) -> isize {
17588    // musl declares readahead() as ssize_t; returning int leaves the upper
17589    // half of rax undefined for callers that expect a 64-bit result.
17590    syscall_ret(<Arch as Syscalls>::syscall3(sysno(187, 213), fd as i64, offset, count as i64)) as isize
17591}
17592
17593#[no_mangle]
17594pub unsafe extern "C" fn fallocate(fd: i32, mode: i32, offset: i64, len: u64) -> i32 {
17595    syscall_ret(<Arch as Syscalls>::syscall4(SYS_FALLOCATE, fd as i64, mode as i64, offset, len as i64)) as i32
17596}
17597
17598// ============================================================
17599// Terminal functions
17600// ============================================================
17601
17602#[no_mangle]
17603pub unsafe extern "C" fn tcgetattr(fd: i32, termios_p: *mut core::ffi::c_void) -> i32 {
17604    syscall_ret(<Arch as Syscalls>::syscall3(SYS_IOCTL, fd as i64, 0x5401, termios_p as i64)) as i32
17605}
17606
17607#[no_mangle]
17608pub unsafe extern "C" fn tcsetattr(fd: i32, optional_actions: i32, termios_p: *const core::ffi::c_void) -> i32 {
17609    let cmd = match optional_actions {
17610        0 => 0x5402,
17611        1 => 0x5403,
17612        2 => 0x5404,
17613        _ => 0x5402,
17614    };
17615    syscall_ret(<Arch as Syscalls>::syscall3(SYS_IOCTL, fd as i64, cmd, termios_p as i64)) as i32
17616}
17617
17618#[no_mangle]
17619pub unsafe extern "C" fn tcdrain(fd: i32) -> i32 {
17620    syscall_ret(<Arch as Syscalls>::syscall3(SYS_IOCTL, fd as i64, 0x5409, 1)) as i32
17621}
17622
17623#[no_mangle]
17624pub unsafe extern "C" fn tcflush(fd: i32, queue_selector: i32) -> i32 {
17625    syscall_ret(<Arch as Syscalls>::syscall3(SYS_IOCTL, fd as i64, 0x540B, queue_selector as i64)) as i32
17626}
17627
17628#[no_mangle]
17629pub unsafe extern "C" fn tcflow(fd: i32, action: i32) -> i32 {
17630    syscall_ret(<Arch as Syscalls>::syscall3(SYS_IOCTL, fd as i64, 0x540A, action as i64)) as i32
17631}
17632
17633#[no_mangle]
17634pub unsafe extern "C" fn tcsendbreak(fd: i32, duration: i32) -> i32 {
17635    syscall_ret(<Arch as Syscalls>::syscall3(SYS_IOCTL, fd as i64, 0x5425, duration as i64)) as i32
17636}
17637
17638#[no_mangle]
17639pub unsafe extern "C" fn tcgetsid(fd: i32) -> i32 {
17640    let mut sid: i32 = 0;
17641    let r = <Arch as Syscalls>::syscall3(SYS_IOCTL, fd as i64, 0x5429, &mut sid as *mut i32 as i64);
17642    if r < 0 { -1 } else { sid }
17643}
17644
17645#[no_mangle]
17646pub unsafe extern "C" fn tcgetpgrp(fd: i32) -> i32 {
17647    let mut pgrp: i32 = 0;
17648    let r = <Arch as Syscalls>::syscall3(SYS_IOCTL, fd as i64, 0x540F, &mut pgrp as *mut i32 as i64);
17649    if r < 0 { -1 } else { pgrp }
17650}
17651
17652#[no_mangle]
17653pub unsafe extern "C" fn tcsetpgrp(fd: i32, pgrp: i32) -> i32 {
17654    syscall_ret(<Arch as Syscalls>::syscall3(SYS_IOCTL, fd as i64, 0x5410, &pgrp as *const i32 as i64)) as i32
17655}
17656
17657// ============================================================
17658// Memory locking functions
17659// ============================================================
17660
17661// ============================================================
17662
17663// ============================================================
17664// POSIX message queue functions
17665// ============================================================
17666
17667#[no_mangle]
17668pub unsafe extern "C" fn mq_open(name: *const core::ffi::c_char, oflag: i32, mut args: ...) -> i32 {
17669    if name.is_null() { ERRNO!() = EINVAL; return -1; }
17670    let mut p = name as *const u8;
17671    if *p == b'/' { p = p.add(1); }
17672    let mut mode: u32 = 0;
17673    let mut attr: *const core::ffi::c_void = core::ptr::null();
17674    if oflag & 0o100 != 0 {
17675        mode = args.next_arg::<u32>();
17676        attr = args.next_arg::<*const core::ffi::c_void>();
17677    }
17678    syscall_ret(<Arch as Syscalls>::syscall4(
17679        sysno(240, 180),
17680        p as i64,
17681        oflag as i64,
17682        mode as i64,
17683        attr as i64,
17684    )) as i32
17685}
17686
17687#[no_mangle]
17688pub unsafe extern "C" fn mq_close(mqdes: i32) -> i32 {
17689    crate::close(mqdes as i32)
17690}
17691
17692#[no_mangle]
17693pub unsafe extern "C" fn mq_unlink(name: *const core::ffi::c_char) -> i32 {
17694    if name.is_null() { ERRNO!() = EINVAL; return -1; }
17695    let mut p = name as *const u8;
17696    if *p == b'/' { p = p.add(1); }
17697    let r = <Arch as Syscalls>::syscall1(sysno(241, 181), p as i64);
17698    if r < 0 {
17699        // The kernel reports EPERM for an unlink while the queue is open.
17700        let e = if (-r) as c_int == 1 { 13 } else { (-r) as c_int };
17701        ERRNO!() = e;
17702        -1
17703    } else {
17704        0
17705    }
17706}
17707
17708#[no_mangle]
17709pub unsafe extern "C" fn mq_send(mqdes: i32, msg_ptr: *const core::ffi::c_char, msg_len: usize, msg_prio: u32) -> i32 {
17710    mq_timedsend(mqdes, msg_ptr, msg_len, msg_prio, core::ptr::null())
17711}
17712
17713#[no_mangle]
17714pub unsafe extern "C" fn mq_receive(mqdes: i32, msg_ptr: *mut core::ffi::c_char, msg_len: usize, msg_prio: *mut u32) -> isize {
17715    mq_timedreceive(mqdes, msg_ptr, msg_len, msg_prio, core::ptr::null())
17716}
17717
17718#[no_mangle]
17719pub unsafe extern "C" fn mq_timedsend(
17720    mqdes: i32,
17721    msg_ptr: *const core::ffi::c_char,
17722    msg_len: usize,
17723    msg_prio: u32,
17724    abs_timeout: *const core::ffi::c_void,
17725) -> i32 {
17726    syscall_ret(<Arch as Syscalls>::syscall5(
17727        sysno(242, 182),
17728        mqdes as i64,
17729        msg_ptr as i64,
17730        msg_len as i64,
17731        msg_prio as i64,
17732        abs_timeout as i64,
17733    )) as i32
17734}
17735
17736#[no_mangle]
17737pub unsafe extern "C" fn mq_timedreceive(
17738    mqdes: i32,
17739    msg_ptr: *mut core::ffi::c_char,
17740    msg_len: usize,
17741    msg_prio: *mut u32,
17742    abs_timeout: *const core::ffi::c_void,
17743) -> isize {
17744    syscall_ret(<Arch as Syscalls>::syscall5(
17745        sysno(243, 183),
17746        mqdes as i64,
17747        msg_ptr as i64,
17748        msg_len as i64,
17749        msg_prio as i64,
17750        abs_timeout as i64,
17751    )) as isize
17752}
17753
17754#[repr(C)]
17755pub struct SigEventFull {
17756    sigev_value: usize,
17757    sigev_signo: i32,
17758    sigev_notify: i32,
17759    sigev_function: usize,
17760    sigev_attributes: usize,
17761    sigev_pad: [u8; 32],
17762}
17763
17764struct MqNotifyArgs {
17765    sem: sem_t,
17766    sock: i32,
17767    mqd: i32,
17768    err: i32,
17769    func: usize,
17770    value: usize,
17771}
17772
17773unsafe extern "C" fn mq_notify_thread(arg: *mut c_void) -> *mut c_void {
17774    let a = &mut *(arg as *mut MqNotifyArgs);
17775    let sock = a.sock;
17776    let mqd = a.mqd;
17777    let func = a.func;
17778    let value = a.value;
17779    let zeros = [0u8; 32];
17780    let sev2 = SigEventFull {
17781        sigev_value: zeros.as_ptr() as usize,
17782        sigev_signo: sock,
17783        sigev_notify: 2, // SIGEV_THREAD, consumed by the kernel as a netlink fd
17784        sigev_function: 0,
17785        sigev_attributes: 0,
17786        sigev_pad: [0; 32],
17787    };
17788    let r = <Arch as Syscalls>::syscall2(sysno(244, 184), mqd as i64, &sev2 as *const SigEventFull as i64);
17789    a.err = if r < 0 { (-r) as i32 } else { 0 };
17790    sem_post(&mut a.sem);
17791    if a.err != 0 { return core::ptr::null_mut(); }
17792    pthread_detach(pthread_self());
17793    let mut buf = [0u8; 32];
17794    let n = recv(sock, buf.as_mut_ptr() as *mut c_void, buf.len(), 0x4000 | 0x100);
17795    crate::close(sock);
17796    if n as usize == buf.len() && buf[buf.len() - 1] == 1 && func != 0 {
17797        let f: extern "C" fn(usize) = core::mem::transmute(func);
17798        f(value);
17799    }
17800    core::ptr::null_mut()
17801}
17802
17803#[no_mangle]
17804pub unsafe extern "C" fn mq_notify(mqdes: i32, sev: *const core::ffi::c_void) -> i32 {
17805    if sev.is_null() {
17806        return syscall_ret(<Arch as Syscalls>::syscall2(sysno(244, 184), mqdes as i64, 0)) as i32;
17807    }
17808    let ev = &*(sev as *const SigEventFull);
17809    if ev.sigev_notify != 2 {
17810        return syscall_ret(<Arch as Syscalls>::syscall2(sysno(244, 184), mqdes as i64, sev as i64)) as i32;
17811    }
17812
17813    // SIGEV_THREAD is emulated with a netlink socket and a helper thread,
17814    // matching musl. The kernel only deals with the socket.
17815    let sock = socket(16 /* AF_NETLINK */, 3 | 0x80000 /* SOCK_RAW|SOCK_CLOEXEC */, 0);
17816    if sock < 0 { return -1; }
17817    let mut a = MqNotifyArgs {
17818        sem: core::mem::zeroed(),
17819        sock,
17820        mqd: mqdes,
17821        err: 0,
17822        func: ev.sigev_function,
17823        value: ev.sigev_value,
17824    };
17825    sem_init(&mut a.sem, 0, 0);
17826
17827    let mut attr: pthread_attr_t = core::mem::zeroed();
17828    if ev.sigev_attributes != 0 {
17829        attr = core::ptr::read(ev.sigev_attributes as *const pthread_attr_t);
17830    } else {
17831        pthread_attr_init(&mut attr);
17832    }
17833    pthread_attr_setdetachstate(&mut attr, PTHREAD_CREATE_JOINABLE);
17834
17835    let mut all: SigSetT = 0;
17836    sigfillset(&mut all);
17837    let mut orig: SigSetT = 0;
17838    pthread_sigmask(0 /* SIG_BLOCK */, &all, &mut orig);
17839
17840    let mut td: PthreadT = 0;
17841    let r = pthread_create(&mut td, &attr, mq_notify_thread as *const () as usize, &mut a as *mut MqNotifyArgs as *mut c_void);
17842    pthread_sigmask(2 /* SIG_SETMASK */, &orig, core::ptr::null_mut());
17843    if r != 0 {
17844        crate::close(sock);
17845        ERRNO!() = 11;
17846        return -1;
17847    }
17848    sem_wait(&mut a.sem);
17849    sem_destroy(&mut a.sem);
17850    if a.err != 0 {
17851        crate::close(sock);
17852        pthread_join(td, core::ptr::null_mut());
17853        ERRNO!() = a.err;
17854        return -1;
17855    }
17856    0
17857}
17858
17859#[no_mangle]
17860pub unsafe extern "C" fn mq_getattr(mqdes: i32, attr: *mut core::ffi::c_void) -> i32 {
17861    mq_setattr(mqdes, core::ptr::null(), attr)
17862}
17863
17864#[no_mangle]
17865pub unsafe extern "C" fn mq_setattr(
17866    mqdes: i32,
17867    new: *const core::ffi::c_void,
17868    old: *mut core::ffi::c_void,
17869) -> i32 {
17870    syscall_ret(<Arch as Syscalls>::syscall3(
17871        sysno(245, 185),
17872        mqdes as i64,
17873        new as i64,
17874        old as i64,
17875    )) as i32
17876}
17877
17878// ============================================================
17879// pthread functions
17880// ============================================================
17881
17882#[no_mangle]
17883pub unsafe extern "C" fn pthread_getaffinity_np(thread: usize, cpusetsize: usize, cpuset: *mut u8) -> i32 {
17884    sched_getaffinity(thread as i32, cpusetsize, cpuset)
17885}
17886
17887#[no_mangle]
17888pub unsafe extern "C" fn pthread_setaffinity_np(thread: usize, cpusetsize: usize, cpuset: *const u8) -> i32 {
17889    sched_setaffinity(thread as i32, cpusetsize, cpuset)
17890}
17891
17892// ============================================================
17893// sched functions
17894// ============================================================
17895
17896#[no_mangle]
17897pub unsafe extern "C" fn sched_yield() -> i32 {
17898    syscall_ret(<Arch as Syscalls>::syscall0(sysno(24, 124))) as i32
17899}
17900
17901#[no_mangle]
17902pub unsafe extern "C" fn sched_get_priority_max(policy: i32) -> i32 {
17903    syscall_ret(<Arch as Syscalls>::syscall1(sysno(146, 125), policy as i64)) as i32
17904}
17905
17906#[no_mangle]
17907pub unsafe extern "C" fn sched_get_priority_min(policy: i32) -> i32 {
17908    syscall_ret(<Arch as Syscalls>::syscall1(sysno(147, 126), policy as i64)) as i32
17909}
17910
17911#[no_mangle]
17912pub unsafe extern "C" fn sched_getparam(pid: i32, param: *mut core::ffi::c_void) -> i32 {
17913    syscall_ret(<Arch as Syscalls>::syscall2(sysno(143, 121), pid as i64, param as i64)) as i32
17914}
17915
17916#[no_mangle]
17917pub unsafe extern "C" fn sched_setparam(pid: i32, param: *const core::ffi::c_void) -> i32 {
17918    syscall_ret(<Arch as Syscalls>::syscall2(sysno(142, 118), pid as i64, param as i64)) as i32
17919}
17920
17921#[no_mangle]
17922pub unsafe extern "C" fn sched_getscheduler(pid: i32) -> i32 {
17923    syscall_ret(<Arch as Syscalls>::syscall1(sysno(145, 120), pid as i64)) as i32
17924}
17925
17926#[no_mangle]
17927pub unsafe extern "C" fn sched_setscheduler(pid: i32, policy: i32, param: *const core::ffi::c_void) -> i32 {
17928    syscall_ret(<Arch as Syscalls>::syscall3(sysno(144, 119), pid as i64, policy as i64, param as i64)) as i32
17929}
17930
17931#[no_mangle]
17932pub unsafe extern "C" fn sched_setaffinity(pid: i32, cpusetsize: usize, cpuset: *const u8) -> i32 {
17933    syscall_ret(<Arch as Syscalls>::syscall3(sysno(203, 122), pid as i64, cpusetsize as i64, cpuset as i64)) as i32
17934}
17935
17936#[no_mangle]
17937pub unsafe extern "C" fn sched_getaffinity(pid: i32, cpusetsize: usize, cpuset: *mut u8) -> i32 {
17938    let r = <Arch as Syscalls>::syscall3(sysno(204, 123), pid as i64, cpusetsize as i64, cpuset as i64);
17939    if r < 0 {
17940        ERRNO!() = (-r) as c_int;
17941        return -1;
17942    }
17943    // The kernel returns the number of bytes copied, not 0.
17944    0
17945}
17946
17947#[no_mangle]
17948pub unsafe extern "C" fn sched_getcpu() -> i32 {
17949    let mut cpu: i32 = 0;
17950    <Arch as Syscalls>::syscall3(sysno(309, 168), &mut cpu as *mut i32 as i64, 0, 0);
17951    cpu
17952}
17953
17954#[no_mangle]
17955pub unsafe extern "C" fn sched_rr_get_interval(pid: i32, tp: *mut core::ffi::c_void) -> i32 {
17956    syscall_ret(<Arch as Syscalls>::syscall2(sysno(148, 127), pid as i64, tp as i64)) as i32
17957}
17958
17959// ============================================================
17960// timer/clock functions
17961// ============================================================
17962
17963#[no_mangle]
17964pub unsafe extern "C" fn timer_create(clockid: i32, sevp: *mut core::ffi::c_void, timerid: *mut i32) -> i32 {
17965    syscall_ret(<Arch as Syscalls>::syscall3(sysno(222, 107), clockid as i64, sevp as i64, timerid as i64)) as i32
17966}
17967
17968#[no_mangle]
17969pub unsafe extern "C" fn timer_delete(timerid: i32) -> i32 {
17970    syscall_ret(<Arch as Syscalls>::syscall1(sysno(226, 111), timerid as i64)) as i32
17971}
17972
17973#[no_mangle]
17974pub unsafe extern "C" fn timer_settime(timerid: i32, flags: i32, new_value: *const core::ffi::c_void, old_value: *mut core::ffi::c_void) -> i32 {
17975    syscall_ret(<Arch as Syscalls>::syscall4(sysno(223, 110), timerid as i64, flags as i64, new_value as i64, old_value as i64)) as i32
17976}
17977
17978#[no_mangle]
17979pub unsafe extern "C" fn timer_gettime(timerid: i32, curr_value: *mut core::ffi::c_void) -> i32 {
17980    syscall_ret(<Arch as Syscalls>::syscall2(sysno(224, 108), timerid as i64, curr_value as i64)) as i32
17981}
17982
17983#[no_mangle]
17984pub unsafe extern "C" fn timer_getoverrun(timerid: i32) -> i32 {
17985    syscall_ret(<Arch as Syscalls>::syscall1(sysno(225, 109), timerid as i64)) as i32
17986}
17987
17988#[no_mangle]
17989pub unsafe extern "C" fn clock_adjtime(_clockid: i32, buf: *mut core::ffi::c_void) -> i32 {
17990    syscall_ret(<Arch as Syscalls>::syscall2(SYS_ADJTIMEX, buf as i64, 0)) as i32
17991}
17992
17993#[no_mangle]
17994pub unsafe extern "C" fn clock_getcpuclockid(pid: i32, clockid: *mut i32) -> i32 {
17995    if clockid.is_null() { return EINVAL; }
17996    let id: i32 = ((-(pid as i64) - 1) * 8 + 2) as i32;
17997    let mut ts: timespec = core::mem::zeroed();
17998    let r = <Arch as Syscalls>::syscall2(SYS_CLOCK_GETRES, id as i64, &mut ts as *mut timespec as i64);
17999    if r < 0 {
18000        let e = (-r) as c_int;
18001        return if e == EINVAL { 3 } else { e };
18002    }
18003    *clockid = id;
18004    0
18005}
18006
18007// ============================================================
18008// String functions
18009// ============================================================
18010
18011#[no_mangle]
18012pub unsafe extern "C" fn strcoll(s1: *const core::ffi::c_char, s2: *const core::ffi::c_char) -> i32 {
18013    // ponytail: simplified collation - just strcmp
18014    let mut i = 0;
18015    loop {
18016        let c1 = *s1.add(i) as u8;
18017        let c2 = *s2.add(i) as u8;
18018        if c1 == 0 || c2 == 0 { return (c1 as i32) - (c2 as i32); }
18019        if c1 != c2 { return (c1 as i32) - (c2 as i32); }
18020        i += 1;
18021    }
18022}
18023
18024#[no_mangle]
18025pub unsafe extern "C" fn strxfrm(dest: *mut core::ffi::c_char, src: *const core::ffi::c_char, n: usize) -> usize {
18026    let mut i = 0;
18027    while i < n {
18028        let c = *src.add(i);
18029        *dest.add(i) = c;
18030        if c == 0 { break; }
18031        i += 1;
18032    }
18033    i
18034}
18035
18036// ============================================================
18037// Directory functions
18038// ============================================================
18039
18040#[no_mangle]
18041pub unsafe extern "C" fn scandir(dirp: *const core::ffi::c_char, _namelist: *mut *mut *mut core::ffi::c_void, _filter: Option<unsafe extern "C" fn(*const core::ffi::c_void) -> i32>, _compar: Option<unsafe extern "C" fn(*const core::ffi::c_void, *const core::ffi::c_void) -> i32>) -> i32 {
18042    let dir = opendir(dirp);
18043    if dir.is_null() { return -1; }
18044    let mut count = 0;
18045    loop {
18046        let ent = readdir(dir);
18047        if ent.is_null() { break; }
18048        count += 1;
18049    }
18050    closedir(dir);
18051    count
18052}
18053
18054#[no_mangle]
18055pub unsafe extern "C" fn alphasort(a: *const *const core::ffi::c_void, b: *const *const core::ffi::c_void) -> i32 {
18056    let s1 = *a as *const core::ffi::c_char;
18057    let s2 = *b as *const core::ffi::c_char;
18058    crate::strcmp(s1 as *const u8, s2 as *const u8)
18059}
18060
18061#[no_mangle]
18062pub unsafe extern "C" fn versionsort(a: *const *const core::ffi::c_void, b: *const *const core::ffi::c_void) -> i32 {
18063    let s1 = *a as *const core::ffi::c_char;
18064    let s2 = *b as *const core::ffi::c_char;
18065    crate::strcmp(s1 as *const u8, s2 as *const u8)
18066}
18067
18068// ============================================================
18069
18070
18071// ============================================================
18072// err functions
18073// ============================================================
18074
18075unsafe fn vwarn_impl(fmt: *const c_char, ap: VaList, with_errno: bool) {
18076    let mut buf = [0 as c_char; 1024];
18077    let n = vsnprintf(buf.as_mut_ptr(), buf.len(), fmt, ap);
18078    if n > 0 {
18079        let l = core::cmp::min(n as usize, buf.len() - 1);
18080        crate::write(2, buf.as_ptr() as *const c_void, l);
18081    }
18082    if with_errno {
18083        let e = ERRNO!();
18084        crate::write(2, b": ".as_ptr() as *const c_void, 2);
18085        let msg = strerror(e);
18086        crate::write(2, msg as *const c_void, crate::strlen(msg));
18087    }
18088    crate::write(2, b"\n".as_ptr() as *const c_void, 1);
18089}
18090
18091#[no_mangle]
18092pub unsafe extern "C" fn verr(eval: c_int, fmt: *const c_char, ap: VaList) {
18093    vwarn_impl(fmt, ap, true);
18094    crate::exit(eval);
18095}
18096
18097#[no_mangle]
18098pub unsafe extern "C" fn verrx(eval: c_int, fmt: *const c_char, ap: VaList) {
18099    vwarn_impl(fmt, ap, false);
18100    crate::exit(eval);
18101}
18102
18103#[no_mangle]
18104pub unsafe extern "C" fn vwarn(fmt: *const c_char, ap: VaList) {
18105    vwarn_impl(fmt, ap, true);
18106}
18107
18108#[no_mangle]
18109pub unsafe extern "C" fn vwarnx(fmt: *const c_char, ap: VaList) {
18110    vwarn_impl(fmt, ap, false);
18111}
18112
18113#[no_mangle]
18114pub unsafe extern "C" fn err(eval: c_int, fmt: *const c_char, args: ...) {
18115    verr(eval, fmt, args);
18116}
18117
18118#[no_mangle]
18119pub unsafe extern "C" fn errx(eval: c_int, fmt: *const c_char, args: ...) {
18120    verrx(eval, fmt, args);
18121}
18122
18123#[no_mangle]
18124pub unsafe extern "C" fn warn(fmt: *const c_char, args: ...) {
18125    vwarn(fmt, args);
18126}
18127
18128#[no_mangle]
18129pub unsafe extern "C" fn warnx(fmt: *const c_char, args: ...) {
18130    vwarnx(fmt, args);
18131}
18132
18133// ============================================================
18134// ecvt functions
18135// ============================================================
18136
18137#[no_mangle]
18138pub unsafe extern "C" fn ecvt(value: f64, ndigit: i32, decpt: *mut i32, sign: *mut i32) -> *mut core::ffi::c_char {
18139    static mut BUF: [core::ffi::c_char; 80] = [0; 80];
18140    if value < 0.0 { *sign = 1; } else { *sign = 0; }
18141    let v = if value < 0.0 { -value } else { value };
18142    if v == 0.0 { *decpt = 0; BUF[0] = 0; return BUF.as_mut_ptr(); }
18143    let mut d = 0i32;
18144    let mut tmp = v;
18145    while tmp >= 10.0 { tmp /= 10.0; d += 1; }
18146    while tmp < 1.0 { tmp *= 10.0; d -= 1; }
18147    *decpt = d + 1;
18148    let mut i = 0;
18149    let mut val = tmp;
18150    while i < ndigit as usize && i < 79 {
18151        let digit = val as i32;
18152        BUF[i] = (b'0' + digit as u8) as c_char;
18153        val = (val - digit as f64) * 10.0;
18154        i += 1;
18155    }
18156    BUF[i] = 0;
18157    BUF.as_mut_ptr()
18158}
18159
18160#[no_mangle]
18161pub unsafe extern "C" fn fcvt(value: f64, ndigit: i32, decpt: *mut i32, sign: *mut i32) -> *mut core::ffi::c_char {
18162    static mut BUF: [core::ffi::c_char; 80] = [0; 80];
18163    if value < 0.0 { *sign = 1; } else { *sign = 0; }
18164    let v = if value < 0.0 { -value } else { value };
18165    if v == 0.0 { *decpt = 0; BUF[0] = 0; return BUF.as_mut_ptr(); }
18166    let mut d = 0i32;
18167    let mut tmp = v;
18168    while tmp >= 10.0 { tmp /= 10.0; d += 1; }
18169    while tmp < 1.0 { tmp *= 10.0; d -= 1; }
18170    *decpt = d + 1;
18171    let mut val = tmp;
18172    let mut i = 0;
18173    while i < (*decpt) as usize && i < 79 {
18174        let digit = val as i32;
18175        BUF[i] = (b'0' + digit as u8) as c_char;
18176        val = (val - digit as f64) * 10.0;
18177        i += 1;
18178    }
18179    if ndigit > 0 && i < 79 {
18180        BUF[i] = '.' as c_char;
18181        i += 1;
18182        let mut frac = val;
18183        let mut j = 0;
18184        while j < ndigit as usize && i < 79 {
18185            let digit = frac as i32;
18186            BUF[i] = (b'0' + digit as u8) as c_char;
18187            frac = (frac - digit as f64) * 10.0;
18188            i += 1;
18189            j += 1;
18190        }
18191    }
18192    BUF[i] = 0;
18193    BUF.as_mut_ptr()
18194}
18195
18196#[no_mangle]
18197pub unsafe extern "C" fn gcvt(value: f64, _ndigit: i32, buf: *mut core::ffi::c_char) -> *mut core::ffi::c_char {
18198    // Simple implementation - convert to string
18199    if value == 0.0 {
18200        *buf = '0' as c_char;
18201        *buf.add(1) = 0;
18202        return buf;
18203    }
18204    let mut i = 0;
18205    let v = if value < 0.0 { *buf = '-' as c_char; i = 1; -value } else { value };
18206    // Integer part
18207    let mut int_part = v as i64;
18208    let mut digits = [0u8; 32];
18209    let mut di = 0;
18210    if int_part == 0 {
18211        digits[0] = 0;
18212        di = 1;
18213    } else {
18214        while int_part > 0 && di < 32 {
18215            digits[di] = (int_part % 10) as u8;
18216            int_part /= 10;
18217            di += 1;
18218        }
18219    }
18220    while di > 0 && i < 79 {
18221        di -= 1;
18222        *buf.add(i) = (b'0' + digits[di]) as c_char;
18223        i += 1;
18224    }
18225    *buf.add(i) = 0;
18226    buf
18227}
18228
18229// ============================================================
18230// usershell functions
18231// ============================================================
18232
18233static mut SHELLS: [*const core::ffi::c_char; 3] = [
18234    b"/bin/sh\0".as_ptr() as *const core::ffi::c_char,
18235    b"/bin/bash\0".as_ptr() as *const core::ffi::c_char,
18236    core::ptr::null(),
18237];
18238static mut SHELL_IDX: usize = 0;
18239
18240#[no_mangle]
18241pub unsafe extern "C" fn getusershell() -> *mut core::ffi::c_char {
18242    let s = SHELLS[SHELL_IDX];
18243    if s.is_null() {
18244        SHELL_IDX = 0;
18245        return core::ptr::null_mut();
18246    }
18247    SHELL_IDX += 1;
18248    s as *mut core::ffi::c_char
18249}
18250
18251#[no_mangle]
18252pub unsafe extern "C" fn setusershell() {
18253    SHELL_IDX = 0;
18254}
18255
18256#[no_mangle]
18257pub unsafe extern "C" fn endusershell() {
18258    SHELL_IDX = 0;
18259}
18260
18261// ============================================================
18262// ether functions
18263// ============================================================
18264
18265#[no_mangle]
18266pub unsafe extern "C" fn ether_aton(s: *const core::ffi::c_char) -> *mut u8 {
18267    static mut EA: [u8; 6] = [0; 6];
18268    if ether_aton_r(s, EA.as_mut_ptr()).is_null() { core::ptr::null_mut() } else { EA.as_mut_ptr() }
18269}
18270
18271#[no_mangle]
18272pub unsafe extern "C" fn ether_aton_r(s: *const core::ffi::c_char, ea: *mut u8) -> *mut u8 {
18273    if ea.is_null() { return core::ptr::null_mut(); }
18274    let mut i = 0;
18275    let mut j = 0;
18276    let mut val: u32 = 0;
18277    let mut digits = 0;
18278    while *s.add(i) != 0 && j < 6 {
18279        let c = *s.add(i);
18280        if c == ':' as c_char {
18281            if digits == 0 { return core::ptr::null_mut(); }
18282            *ea.add(j) = val as u8;
18283            j += 1;
18284            val = 0;
18285            digits = 0;
18286        } else if (c >= '0' as c_char && c <= '9' as c_char) || (c >= 'a' as c_char && c <= 'f' as c_char) || (c >= 'A' as c_char && c <= 'F' as c_char) {
18287            let d = if c >= '0' as c_char && c <= '9' as c_char { (c - '0' as c_char) as u32 }
18288                   else if c >= 'a' as c_char { (c - 'a' as c_char + 10) as u32 }
18289                   else { (c - 'A' as c_char + 10) as u32 };
18290            val = val * 16 + d;
18291            if val > 255 { return core::ptr::null_mut(); }
18292            digits += 1;
18293            if digits > 2 { return core::ptr::null_mut(); }
18294        } else {
18295            return core::ptr::null_mut();
18296        }
18297        i += 1;
18298    }
18299    if digits == 0 || j != 5 { return core::ptr::null_mut(); }
18300    *ea.add(j) = val as u8;
18301    ea
18302}
18303
18304#[no_mangle]
18305pub unsafe extern "C" fn ether_ntoa(ea: *const u8) -> *mut core::ffi::c_char {
18306    static mut BUF: [core::ffi::c_char; 18] = [0; 18];
18307    ether_ntoa_r(ea, BUF.as_mut_ptr());
18308    BUF.as_mut_ptr()
18309}
18310
18311#[no_mangle]
18312pub unsafe extern "C" fn ether_ntoa_r(ea: *const u8, buf: *mut core::ffi::c_char) -> *mut core::ffi::c_char {
18313    let a = *ea.add(0); let b = *ea.add(1); let c = *ea.add(2);
18314    let d = *ea.add(3); let e = *ea.add(4); let f = *ea.add(5);
18315    let hex = b"0123456789abcdef\0".as_ptr() as *const u8;
18316    let mut i = 0;
18317    *buf.add(i) = *hex.add((a >> 4) as usize) as c_char; i += 1;
18318    *buf.add(i) = *hex.add((a & 0xf) as usize) as c_char; i += 1;
18319    *buf.add(i) = ':' as c_char; i += 1;
18320    *buf.add(i) = *hex.add((b >> 4) as usize) as c_char; i += 1;
18321    *buf.add(i) = *hex.add((b & 0xf) as usize) as c_char; i += 1;
18322    *buf.add(i) = ':' as c_char; i += 1;
18323    *buf.add(i) = *hex.add((c >> 4) as usize) as c_char; i += 1;
18324    *buf.add(i) = *hex.add((c & 0xf) as usize) as c_char; i += 1;
18325    *buf.add(i) = ':' as c_char; i += 1;
18326    *buf.add(i) = *hex.add((d >> 4) as usize) as c_char; i += 1;
18327    *buf.add(i) = *hex.add((d & 0xf) as usize) as c_char; i += 1;
18328    *buf.add(i) = ':' as c_char; i += 1;
18329    *buf.add(i) = *hex.add((e >> 4) as usize) as c_char; i += 1;
18330    *buf.add(i) = *hex.add((e & 0xf) as usize) as c_char; i += 1;
18331    *buf.add(i) = ':' as c_char; i += 1;
18332    *buf.add(i) = *hex.add((f >> 4) as usize) as c_char; i += 1;
18333    *buf.add(i) = *hex.add((f & 0xf) as usize) as c_char; i += 1;
18334    *buf.add(i) = 0;
18335    buf
18336}
18337
18338// ============================================================
18339// utmpx functions
18340// ============================================================
18341
18342#[no_mangle]
18343pub unsafe extern "C" fn getutxent() -> *mut core::ffi::c_void {
18344    getutent()
18345}
18346
18347#[no_mangle]
18348pub unsafe extern "C" fn getutxid(ut: *const core::ffi::c_void) -> *mut core::ffi::c_void {
18349    getutid(ut)
18350}
18351
18352#[no_mangle]
18353pub unsafe extern "C" fn getutxline(ut: *const core::ffi::c_void) -> *mut core::ffi::c_void {
18354    getutline(ut)
18355}
18356
18357#[no_mangle]
18358pub unsafe extern "C" fn pututxline(ut: *const core::ffi::c_void) -> *mut core::ffi::c_void {
18359    pututline(ut)
18360}
18361
18362#[no_mangle]
18363pub unsafe extern "C" fn setutxent() {
18364    setutent()
18365}
18366
18367#[no_mangle]
18368pub unsafe extern "C" fn endutxent() {
18369    endutent()
18370}
18371
18372// ============================================================
18373// exec functions
18374// ============================================================
18375
18376#[no_mangle]
18377pub unsafe extern "C" fn execv(path: *const core::ffi::c_char, argv: *const *const core::ffi::c_char) -> i32 {
18378    crate::execve(path, argv, __environ as *const *const core::ffi::c_char)
18379}
18380
18381#[no_mangle]
18382pub unsafe extern "C" fn execlp(file: *const core::ffi::c_char, _args: ...) -> i32 {
18383    // Build argv from varargs
18384    let mut argv: [*const core::ffi::c_char; 64] = [core::ptr::null(); 64];
18385    let mut i = 0;
18386    let mut args = _args;
18387    loop {
18388        let arg = args.next_arg::<*const core::ffi::c_char>();
18389        if arg.is_null() { break; }
18390        argv[i] = arg;
18391        i += 1;
18392        if i >= 63 { break; }
18393    }
18394    argv[i] = core::ptr::null();
18395    execvp(file, argv.as_ptr())
18396}
18397
18398#[no_mangle]
18399pub unsafe extern "C" fn execvp(file: *const core::ffi::c_char, argv: *const *const core::ffi::c_char) -> i32 {
18400    execvpe(file, argv, __environ as *const *const core::ffi::c_char)
18401}
18402
18403#[no_mangle]
18404pub unsafe extern "C" fn fexecve(fd: i32, argv: *const *const core::ffi::c_char, envp: *const *const core::ffi::c_char) -> i32 {
18405    // Read /proc/self/fd/N to get the path
18406    let mut path: [core::ffi::c_char; 64] = [0; 64];
18407    let mut i = 0;
18408    let src = b"/proc/self/fd/\0".as_ptr() as *const core::ffi::c_char;
18409    while i < 63 && *src.add(i) != 0 { path[i] = *src.add(i); i += 1; }
18410    let mut n = fd as usize;
18411    if n == 0 { path[i] = '0' as c_char; i += 1; }
18412    while n > 0 && i < 63 {
18413        path[i] = (b'0' + (n % 10) as u8) as c_char;
18414        n /= 10;
18415        i += 1;
18416    }
18417    path[i] = 0;
18418    crate::execve(path.as_ptr(), argv, envp)
18419}
18420
18421// ============================================================
18422// conf functions
18423// ============================================================
18424
18425#[no_mangle]
18426pub unsafe extern "C" fn confstr(name: i32, buf: *mut core::ffi::c_char, len: usize) -> usize {
18427    // _CS_PATH is the only value with content; the POSIX_V6/V7 option
18428    // strings are empty on Linux. Truncation follows snprintf semantics.
18429    let value: &[u8] = if name == 0 {
18430        b"/bin:/usr/bin"
18431    } else if (1..=37).contains(&name) {
18432        b""
18433    } else {
18434        ERRNO!() = EINVAL;
18435        return 0;
18436    };
18437    let need = value.len() + 1;
18438    if !buf.is_null() && len > 0 {
18439        let n = core::cmp::min(value.len(), len - 1);
18440        core::ptr::copy_nonoverlapping(value.as_ptr(), buf as *mut u8, n);
18441        *buf.add(n) = 0;
18442    }
18443    need
18444}
18445
18446#[no_mangle]
18447pub unsafe extern "C" fn pathconf(_path: *const core::ffi::c_char, name: i32) -> i64 {
18448    // Return default values
18449    match name {
18450        0 => 4096, // _PC_LINK_MAX
18451        1 => 255,  // _PC_MAX_CANON
18452        2 => 255,  // _PC_MAX_INPUT
18453        3 => 255,  // _PC_NAME_MAX
18454        4 => 4096, // _PC_PATH_MAX
18455        5 => 4096, // _PC_PIPE_BUF
18456        6 => 1,    // _PC_CHOWN_RESTRICTED
18457        7 => 0,    // _PC_NO_TRUNC
18458        8 => 0,    // _PC_VDISABLE
18459        _ => -1,
18460    }
18461}
18462
18463#[no_mangle]
18464pub unsafe extern "C" fn fpathconf(_fd: i32, name: i32) -> i64 {
18465    // Return default values
18466    match name {
18467        0 => 4096, // _PC_LINK_MAX
18468        1 => 255,  // _PC_MAX_CANON
18469        2 => 255,  // _PC_MAX_INPUT
18470        3 => 255,  // _PC_NAME_MAX
18471        4 => 4096, // _PC_PATH_MAX
18472        5 => 4096, // _PC_PIPE_BUF
18473        6 => 1,    // _PC_CHOWN_RESTRICTED
18474        7 => 0,    // _PC_NO_TRUNC
18475        8 => 0,    // _PC_VDISABLE
18476        _ => -1,
18477    }
18478}
18479
18480// ============================================================
18481// exp10 functions
18482// ============================================================
18483
18484#[no_mangle]
18485pub unsafe extern "C" fn exp10(x: f64) -> f64 {
18486    libm::pow(10.0, x)
18487}
18488
18489#[no_mangle]
18490pub unsafe extern "C" fn exp10f(x: f32) -> f32 {
18491    libm::powf(10.0, x)
18492}
18493
18494// ============================================================
18495// memory functions
18496// ============================================================
18497
18498#[no_mangle]
18499pub unsafe extern "C" fn explicit_bzero(s: *mut u8, n: usize) {
18500    core::ptr::write_bytes(s, 0, n);
18501    core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
18502}
18503
18504#[no_mangle]
18505pub unsafe extern "C" fn memccpy(dest: *mut u8, src: *const u8, c: i32, n: usize) -> *mut u8 {
18506    let mut i = 0;
18507    while i < n {
18508        let byte = *src.add(i);
18509        *dest.add(i) = byte;
18510        if byte == c as u8 {
18511            return dest.add(i + 1);
18512        }
18513        i += 1;
18514    }
18515    core::ptr::null_mut()
18516}
18517
18518// ============================================================
18519// *at functions
18520// ============================================================
18521
18522#[no_mangle]
18523pub unsafe extern "C" fn faccessat(dirfd: i32, pathname: *const core::ffi::c_char, mode: i32, flags: i32) -> i32 {
18524    syscall_ret(<Arch as Syscalls>::syscall4(SYS_FACCESSAT, dirfd as i64, pathname as i64, mode as i64, flags as i64)) as i32
18525}
18526
18527#[no_mangle]
18528pub unsafe extern "C" fn fchmodat(dirfd: i32, pathname: *const core::ffi::c_char, mode: u32, flags: i32) -> i32 {
18529    if flags == 0 {
18530        return syscall_ret(<Arch as Syscalls>::syscall4(SYS_FCHMODAT, dirfd as i64, pathname as i64, mode as i64, 0)) as i32;
18531    }
18532    if flags != 0x100 {
18533        // Only AT_SYMLINK_NOFOLLOW is defined for fchmodat.
18534        ERRNO!() = EINVAL;
18535        return -1;
18536    }
18537    syscall_ret(<Arch as Syscalls>::syscall4(sysno(452, 452), dirfd as i64, pathname as i64, mode as i64, flags as i64)) as i32
18538}
18539
18540#[no_mangle]
18541pub unsafe extern "C" fn fchownat(dirfd: i32, pathname: *const core::ffi::c_char, uid: u32, gid: u32, flags: i32) -> i32 {
18542    syscall_ret(<Arch as Syscalls>::syscall5(sysno(260, 54), dirfd as i64, pathname as i64, uid as i64, gid as i64, flags as i64)) as i32
18543}
18544
18545#[no_mangle]
18546pub unsafe extern "C" fn linkat(olddirfd: i32, oldpath: *const core::ffi::c_char, newdirfd: i32, newpath: *const core::ffi::c_char, flags: i32) -> i32 {
18547    syscall_ret(<Arch as Syscalls>::syscall5(SYS_LINKAT, olddirfd as i64, oldpath as i64, newdirfd as i64, newpath as i64, flags as i64)) as i32
18548}
18549
18550#[no_mangle]
18551pub unsafe extern "C" fn mkdirat(dirfd: i32, pathname: *const core::ffi::c_char, mode: u32) -> i32 {
18552    syscall_ret(<Arch as Syscalls>::syscall3(SYS_MKDIRAT, dirfd as i64, pathname as i64, mode as i64)) as i32
18553}
18554
18555#[no_mangle]
18556pub unsafe extern "C" fn mkfifoat(dirfd: i32, pathname: *const core::ffi::c_char, mode: u32) -> i32 {
18557    mknodat(dirfd, pathname, mode | 0o010000, 0)
18558}
18559
18560#[no_mangle]
18561pub unsafe extern "C" fn mknodat(dirfd: i32, pathname: *const core::ffi::c_char, mode: u32, dev: u64) -> i32 {
18562    syscall_ret(<Arch as Syscalls>::syscall4(sysno(259, 33), dirfd as i64, pathname as i64, mode as i64, dev as i64)) as i32
18563}
18564
18565#[no_mangle]
18566pub unsafe extern "C" fn readlinkat(dirfd: i32, pathname: *const core::ffi::c_char, buf: *mut core::ffi::c_char, bufsiz: usize) -> isize {
18567    syscall_ret(<Arch as Syscalls>::syscall4(SYS_READLINKAT, dirfd as i64, pathname as i64, buf as i64, bufsiz as i64)) as isize
18568}
18569
18570#[no_mangle]
18571pub unsafe extern "C" fn renameat(olddirfd: i32, oldpath: *const core::ffi::c_char, newdirfd: i32, newpath: *const core::ffi::c_char) -> i32 {
18572    syscall_ret(<Arch as Syscalls>::syscall5(SYS_RENAMEAT2, olddirfd as i64, oldpath as i64, newdirfd as i64, newpath as i64, 0)) as i32
18573}
18574
18575#[no_mangle]
18576pub unsafe extern "C" fn renameat2(olddirfd: i32, oldpath: *const core::ffi::c_char, newdirfd: i32, newpath: *const core::ffi::c_char, flags: u32) -> i32 {
18577    syscall_ret(<Arch as Syscalls>::syscall5(SYS_RENAMEAT2, olddirfd as i64, oldpath as i64, newdirfd as i64, newpath as i64, flags as i64)) as i32
18578}
18579
18580#[no_mangle]
18581pub unsafe extern "C" fn symlinkat(target: *const core::ffi::c_char, newdirfd: i32, linkpath: *const core::ffi::c_char) -> i32 {
18582    syscall_ret(<Arch as Syscalls>::syscall3(SYS_SYMLINKAT, target as i64, newdirfd as i64, linkpath as i64)) as i32
18583}
18584
18585#[no_mangle]
18586pub unsafe extern "C" fn unlinkat(dirfd: i32, pathname: *const core::ffi::c_char, flags: i32) -> i32 {
18587    syscall_ret(<Arch as Syscalls>::syscall3(SYS_UNLINKAT, dirfd as i64, pathname as i64, flags as i64)) as i32
18588}
18589
18590#[no_mangle]
18591pub unsafe extern "C" fn utimes(filename: *const core::ffi::c_char, times: *const core::ffi::c_void) -> i32 {
18592    utimensat(AT_FDCWD, filename, times as *const timespec, 0)
18593}
18594
18595#[no_mangle]
18596pub unsafe extern "C" fn futimes(fd: i32, times: *const core::ffi::c_void) -> i32 {
18597    utimensat(fd, core::ptr::null(), times as *const timespec, 0)
18598}
18599
18600// ============================================================
18601// inotify functions
18602// ============================================================
18603
18604#[no_mangle]
18605pub unsafe extern "C" fn inotify_init() -> i32 {
18606    syscall_ret(<Arch as Syscalls>::syscall1(sysno(253, 26), 0)) as i32
18607}
18608
18609#[no_mangle]
18610pub unsafe extern "C" fn inotify_init1(flags: i32) -> i32 {
18611    syscall_ret(<Arch as Syscalls>::syscall1(sysno(294, 26), flags as i64)) as i32
18612}
18613
18614#[no_mangle]
18615pub unsafe extern "C" fn inotify_add_watch(fd: i32, pathname: *const core::ffi::c_char, mask: u32) -> i32 {
18616    syscall_ret(<Arch as Syscalls>::syscall3(sysno(254, 27), fd as i64, pathname as i64, mask as i64)) as i32
18617}
18618
18619#[no_mangle]
18620pub unsafe extern "C" fn inotify_rm_watch(fd: i32, wd: i32) -> i32 {
18621    syscall_ret(<Arch as Syscalls>::syscall2(sysno(255, 28), fd as i64, wd as i64)) as i32
18622}
18623
18624#[no_mangle]
18625pub unsafe extern "C" fn fanotify_init(flags: i32, event_f_flags: i32) -> i32 {
18626    syscall_ret(<Arch as Syscalls>::syscall2(sysno(300, 262), flags as i64, event_f_flags as i64)) as i32
18627}
18628
18629#[no_mangle]
18630pub unsafe extern "C" fn fanotify_mark(fd: i32, flags: i32, mask: u64, dirfd: i32, pathname: *const core::ffi::c_char) -> i32 {
18631    syscall_ret(<Arch as Syscalls>::syscall5(sysno(301, 263), fd as i64, flags as i64, mask as i64, dirfd as i64, pathname as i64)) as i32
18632}
18633
18634// ============================================================
18635// ffs functions
18636// ============================================================
18637
18638#[no_mangle]
18639pub unsafe extern "C" fn ffs(x: i32) -> i32 {
18640    if x == 0 { return 0; }
18641    x.trailing_zeros() as i32 + 1
18642}
18643
18644#[no_mangle]
18645pub unsafe extern "C" fn ffsl(x: i64) -> i32 {
18646    if x == 0 { return 0; }
18647    x.trailing_zeros() as i32 + 1
18648}
18649
18650#[no_mangle]
18651pub unsafe extern "C" fn ffsll(x: i64) -> i32 {
18652    if x == 0 { return 0; }
18653    x.trailing_zeros() as i32 + 1
18654}
18655
18656// ============================================================
18657// More system functions
18658// ============================================================
18659
18660#[no_mangle]
18661pub unsafe extern "C" fn fchdir(fd: i32) -> i32 {
18662    syscall_ret(<Arch as Syscalls>::syscall1(SYS_FCHDIR, fd as i64)) as i32
18663}
18664
18665#[no_mangle]
18666pub unsafe extern "C" fn fchown(fd: i32, uid: u32, gid: u32) -> i32 {
18667    syscall_ret(<Arch as Syscalls>::syscall3(SYS_FCHOWN, fd as i64, uid as i64, gid as i64)) as i32
18668}
18669
18670#[no_mangle]
18671pub unsafe extern "C" fn fdopendir(fd: i32) -> *mut core::ffi::c_void {
18672    if fd < 0 { ERRNO!() = 9; return core::ptr::null_mut(); }
18673    dir_stream_new(fd)
18674}
18675
18676#[no_mangle]
18677pub unsafe extern "C" fn getdtablesize() -> i32 {
18678    let mut rlim: Rlimit = core::mem::zeroed();
18679    if getrlimit(RLIMIT_NOFILE, &mut rlim) == 0 {
18680        rlim.rlim_cur as i32
18681    } else {
18682        1024
18683    }
18684}
18685
18686#[no_mangle]
18687pub unsafe extern "C" fn getentropy(buf: *mut u8, buflen: usize) -> i32 {
18688    if buflen > 256 {
18689        ERRNO!() = EIO_VAL;
18690        return -1;
18691    }
18692    let mut pos = buf;
18693    let mut left = buflen;
18694    while left > 0 {
18695        let r = getrandom(pos, left, 0);
18696        if r < 0 {
18697            if ERRNO!() == EINTR_VAL {
18698                continue;
18699            }
18700            return -1;
18701        }
18702        pos = pos.add(r as usize);
18703        left -= r as usize;
18704    }
18705    0
18706}
18707
18708#[no_mangle]
18709pub unsafe extern "C" fn getrandom(buf: *mut u8, buflen: usize, flags: u32) -> isize {
18710    let r = <Arch as Syscalls>::syscall3(sysno(318, 278), buf as i64, buflen as i64, flags as i64);
18711    if r < 0 {
18712        ERRNO!() = (-r) as c_int;
18713        -1
18714    } else {
18715        r as isize
18716    }
18717}
18718
18719#[no_mangle]
18720pub unsafe extern "C" fn killpg(pgrp: i32, sig: i32) -> i32 {
18721    syscall_ret(<Arch as Syscalls>::syscall2(SYS_KILL, -pgrp as i64, sig as i64)) as i32
18722}
18723
18724#[no_mangle]
18725pub unsafe extern "C" fn mkfifo(pathname: *const core::ffi::c_char, mode: u32) -> i32 {
18726    mknod(pathname, 0o010000 | mode, 0)
18727}
18728
18729// ============================================================
18730// Memory allocation functions
18731// ============================================================
18732
18733#[no_mangle]
18734pub unsafe extern "C" fn aligned_alloc(alignment: usize, size: usize) -> *mut core::ffi::c_void {
18735    alloc_aligned_impl(alignment, size) as *mut core::ffi::c_void
18736}
18737
18738#[no_mangle]
18739pub unsafe extern "C" fn memalign(alignment: usize, size: usize) -> *mut core::ffi::c_void {
18740    alloc_aligned_impl(alignment, size) as *mut core::ffi::c_void
18741}
18742
18743#[no_mangle]
18744pub unsafe extern "C" fn posix_memalign(memptr: *mut *mut core::ffi::c_void, alignment: usize, size: usize) -> i32 {
18745    if memptr.is_null() { return EINVAL; }
18746    if alignment < core::mem::size_of::<*mut core::ffi::c_void>() || !alignment.is_power_of_two() {
18747        return EINVAL;
18748    }
18749    let p = alloc_aligned_impl(alignment, size);
18750    if p.is_null() { return ENOMEM; }
18751    *memptr = p as *mut core::ffi::c_void;
18752    0
18753}
18754
18755#[no_mangle]
18756pub unsafe extern "C" fn valloc(size: usize) -> *mut core::ffi::c_void {
18757    alloc_aligned_impl(PAGE, size) as *mut core::ffi::c_void
18758}
18759
18760// ============================================================
18761// glob functions
18762// ============================================================
18763
18764// ============================================================
18765// wordexp functions
18766// ============================================================
18767
18768// ============================================================
18769// gettext functions
18770// ============================================================
18771
18772#[no_mangle]
18773pub unsafe extern "C" fn gettext(msgid: *const core::ffi::c_char) -> *const core::ffi::c_char {
18774    msgid
18775}
18776
18777#[no_mangle]
18778pub unsafe extern "C" fn dgettext(_domainname: *const core::ffi::c_char, msgid: *const core::ffi::c_char) -> *const core::ffi::c_char {
18779    msgid
18780}
18781
18782#[no_mangle]
18783pub unsafe extern "C" fn dcgettext(_domainname: *const core::ffi::c_char, msgid: *const core::ffi::c_char, _category: i32) -> *const core::ffi::c_char {
18784    msgid
18785}
18786
18787#[no_mangle]
18788pub unsafe extern "C" fn ngettext(msgid1: *const core::ffi::c_char, msgid2: *const core::ffi::c_char, n: u64) -> *const core::ffi::c_char {
18789    if n == 1 { msgid1 } else { msgid2 }
18790}
18791
18792#[no_mangle]
18793pub unsafe extern "C" fn dngettext(_domainname: *const core::ffi::c_char, msgid1: *const core::ffi::c_char, msgid2: *const core::ffi::c_char, n: u64) -> *const core::ffi::c_char {
18794    if n == 1 { msgid1 } else { msgid2 }
18795}
18796
18797#[no_mangle]
18798pub unsafe extern "C" fn dcngettext(_domainname: *const core::ffi::c_char, msgid1: *const core::ffi::c_char, msgid2: *const core::ffi::c_char, n: u64, _category: i32) -> *const core::ffi::c_char {
18799    if n == 1 { msgid1 } else { msgid2 }
18800}
18801
18802#[no_mangle]
18803pub unsafe extern "C" fn textdomain(domainname: *const core::ffi::c_char) -> *mut core::ffi::c_char {
18804    static mut DOMAIN: [core::ffi::c_char; 256] = [0; 256];
18805    if domainname.is_null() { return DOMAIN.as_mut_ptr(); }
18806    let mut i = 0;
18807    while i < 255 && *domainname.add(i) != 0 {
18808        DOMAIN[i] = *domainname.add(i);
18809        i += 1;
18810    }
18811    DOMAIN[i] = 0;
18812    DOMAIN.as_mut_ptr()
18813}
18814
18815#[no_mangle]
18816pub unsafe extern "C" fn bindtextdomain(_domainname: *const core::ffi::c_char, dirname: *const core::ffi::c_char) -> *mut core::ffi::c_char {
18817    dirname as *mut core::ffi::c_char
18818}
18819
18820#[no_mangle]
18821pub unsafe extern "C" fn bind_textdomain_codeset(_domainname: *const core::ffi::c_char, codeset: *const core::ffi::c_char) -> *mut core::ffi::c_char {
18822    codeset as *mut core::ffi::c_char
18823}
18824
18825// ============================================================
18826// Wide string functions
18827// ============================================================
18828
18829#[no_mangle]
18830pub unsafe extern "C" fn wmemcpy(dest: *mut u32, src: *const u32, n: usize) -> *mut u32 {
18831    let mut i = 0;
18832    while i < n {
18833        *dest.add(i) = *src.add(i);
18834        i += 1;
18835    }
18836    dest
18837}
18838
18839#[no_mangle]
18840pub unsafe extern "C" fn wmemmove(dest: *mut u32, src: *const u32, n: usize) -> *mut u32 {
18841    if dest as usize <= src as usize {
18842        let mut i = 0;
18843        while i < n {
18844            *dest.add(i) = *src.add(i);
18845            i += 1;
18846        }
18847    } else {
18848        let mut i = n;
18849        while i > 0 {
18850            i -= 1;
18851            *dest.add(i) = *src.add(i);
18852        }
18853    }
18854    dest
18855}
18856
18857#[no_mangle]
18858pub unsafe extern "C" fn wmemset(dest: *mut u32, c: u32, n: usize) -> *mut u32 {
18859    let mut i = 0;
18860    while i < n {
18861        *dest.add(i) = c;
18862        i += 1;
18863    }
18864    dest
18865}
18866
18867#[no_mangle]
18868pub unsafe extern "C" fn wmemcmp(s1: *const u32, s2: *const u32, n: usize) -> i32 {
18869    let mut i = 0;
18870    while i < n {
18871        let c1 = *s1.add(i);
18872        let c2 = *s2.add(i);
18873        if c1 != c2 { return (c1 as i32) - (c2 as i32); }
18874        i += 1;
18875    }
18876    0
18877}
18878
18879#[no_mangle]
18880pub unsafe extern "C" fn wmemchr(s: *const u32, c: u32, n: usize) -> *mut u32 {
18881    let mut i = 0;
18882    while i < n {
18883        if *s.add(i) == c { return s.add(i) as *mut u32; }
18884        i += 1;
18885    }
18886    core::ptr::null_mut()
18887}
18888
18889#[no_mangle]
18890pub unsafe extern "C" fn wcpcpy(dest: *mut u32, src: *const u32) -> *mut u32 {
18891    let mut i = 0;
18892    loop {
18893        let c = *src.add(i);
18894        *dest.add(i) = c;
18895        if c == 0 { return dest.add(i); }
18896        i += 1;
18897    }
18898}
18899
18900#[no_mangle]
18901pub unsafe extern "C" fn wcpncpy(dest: *mut u32, src: *const u32, n: usize) -> *mut u32 {
18902    let mut i = 0;
18903    while i < n {
18904        let c = *src.add(i);
18905        *dest.add(i) = c;
18906        if c == 0 { return dest.add(i); }
18907        i += 1;
18908    }
18909    dest.add(n)
18910}
18911
18912#[no_mangle]
18913pub unsafe extern "C" fn wprintf(fmt: *const wchar_t, args: ...) -> c_int {
18914    vfwprintf(stdout, fmt, args)
18915}
18916
18917#[no_mangle]
18918pub unsafe extern "C" fn vwprintf(fmt: *const wchar_t, ap: core::ffi::VaList) -> c_int {
18919    vfwprintf(stdout, fmt, ap)
18920}
18921
18922// ============================================================
18923// C32 functions
18924// ============================================================
18925
18926#[no_mangle]
18927pub unsafe extern "C" fn c16rtomb(s: *mut u8, c16: u16, ps: *mut core::ffi::c_void) -> usize {
18928    // UTF-16 to UTF-8, with surrogate pairs tracked in the mbstate.
18929    let state = ps as *mut u32;
18930    let saved: u32 = if state.is_null() { 0 } else { *state };
18931    if s.is_null() {
18932        if !state.is_null() { *state = 0; }
18933        return 1;
18934    }
18935    let cp: u32;
18936    if saved != 0 {
18937        if c16 < 0xdc00 || c16 > 0xdfff {
18938            if !state.is_null() { *state = 0; }
18939            ERRNO!() = EILSEQ;
18940            return !0usize;
18941        }
18942        cp = 0x10000 + (((saved - 0xd800) << 10) | ((c16 as u32) - 0xdc00));
18943        if !state.is_null() { *state = 0; }
18944    } else if c16 >= 0xd800 && c16 <= 0xdbff {
18945        if !state.is_null() { *state = c16 as u32; }
18946        return 0;
18947    } else if c16 >= 0xdc00 && c16 <= 0xdfff {
18948        ERRNO!() = EILSEQ;
18949        return !0usize;
18950    } else {
18951        cp = c16 as u32;
18952    }
18953    if cp < 0x80 {
18954        *s = cp as u8;
18955        1
18956    } else if cp < 0x800 {
18957        *s = (0xc0 | (cp >> 6)) as u8;
18958        *s.add(1) = (0x80 | (cp & 0x3f)) as u8;
18959        2
18960    } else if cp < 0x10000 {
18961        *s = (0xe0 | (cp >> 12)) as u8;
18962        *s.add(1) = (0x80 | ((cp >> 6) & 0x3f)) as u8;
18963        *s.add(2) = (0x80 | (cp & 0x3f)) as u8;
18964        3
18965    } else {
18966        *s = (0xf0 | (cp >> 18)) as u8;
18967        *s.add(1) = (0x80 | ((cp >> 12) & 0x3f)) as u8;
18968        *s.add(2) = (0x80 | ((cp >> 6) & 0x3f)) as u8;
18969        *s.add(3) = (0x80 | (cp & 0x3f)) as u8;
18970        4
18971    }
18972}
18973
18974#[no_mangle]
18975pub unsafe extern "C" fn c32rtomb(s: *mut u8, c32: u32, _ps: *mut core::ffi::c_void) -> usize {
18976    if s.is_null() { return 1; }
18977    if c32 > 0x10ffff || (c32 >= 0xd800 && c32 <= 0xdfff) {
18978        ERRNO!() = EILSEQ;
18979        return !0usize;
18980    }
18981    if c32 < 0x80 {
18982        *s = c32 as u8;
18983        1
18984    } else if c32 < 0x800 {
18985        *s = (0xc0 | (c32 >> 6)) as u8;
18986        *s.add(1) = (0x80 | (c32 & 0x3f)) as u8;
18987        2
18988    } else if c32 < 0x10000 {
18989        *s = (0xe0 | (c32 >> 12)) as u8;
18990        *s.add(1) = (0x80 | ((c32 >> 6) & 0x3f)) as u8;
18991        *s.add(2) = (0x80 | (c32 & 0x3f)) as u8;
18992        3
18993    } else {
18994        *s = (0xf0 | (c32 >> 18)) as u8;
18995        *s.add(1) = (0x80 | ((c32 >> 12) & 0x3f)) as u8;
18996        *s.add(2) = (0x80 | ((c32 >> 6) & 0x3f)) as u8;
18997        *s.add(3) = (0x80 | (c32 & 0x3f)) as u8;
18998        4
18999    }
19000}
19001
19002#[no_mangle]
19003pub unsafe extern "C" fn mbrtoc16(pc16: *mut u16, s: *const u8, n: usize, _ps: *mut core::ffi::c_void) -> usize {
19004    if s.is_null() { return 0; }
19005    if n == 0 { return 1; }
19006    let c = *s;
19007    if c == 0 { return 0; }
19008    if c < 0x80 {
19009        if !pc16.is_null() { *pc16 = c as u16; }
19010        return 1;
19011    }
19012    if c < 0xC2 { return 1; } // Invalid
19013    if c < 0xE0 {
19014        if n < 2 { return 1; }
19015        let c2 = *s.add(1);
19016        if (c2 & 0xC0) != 0x80 { return 1; }
19017        if !pc16.is_null() { *pc16 = ((((c & 0x1F) as u32) << 6) | ((c2 & 0x3F) as u32)) as u16; }
19018        return 2;
19019    }
19020    if c < 0xF0 {
19021        if n < 3 { return 1; }
19022        let c2 = *s.add(1);
19023        let c3 = *s.add(2);
19024        if (c2 & 0xC0) != 0x80 || (c3 & 0xC0) != 0x80 { return 1; }
19025        if !pc16.is_null() { *pc16 = ((((c & 0x0F) as u32) << 12) | (((c2 & 0x3F) as u32) << 6) | ((c3 & 0x3F) as u32)) as u16; }
19026        return 3;
19027    }
19028    1
19029}
19030
19031#[no_mangle]
19032pub unsafe extern "C" fn mbrtoc32(pc32: *mut u32, s: *const u8, n: usize, _ps: *mut core::ffi::c_void) -> usize {
19033    if s.is_null() { return 0; }
19034    if n == 0 { return 1; }
19035    let c = *s;
19036    if c == 0 { return 0; }
19037    if c < 0x80 {
19038        if !pc32.is_null() { *pc32 = c as u32; }
19039        return 1;
19040    }
19041    if c < 0xC2 { return 1; } // Invalid
19042    if c < 0xE0 {
19043        if n < 2 { return 1; }
19044        let c2 = *s.add(1);
19045        if (c2 & 0xC0) != 0x80 { return 1; }
19046        if !pc32.is_null() { *pc32 = (((c & 0x1F) as u32) << 6) | ((c2 & 0x3F) as u32); }
19047        return 2;
19048    }
19049    if c < 0xF0 {
19050        if n < 3 { return 1; }
19051        let c2 = *s.add(1);
19052        let c3 = *s.add(2);
19053        if (c2 & 0xC0) != 0x80 || (c3 & 0xC0) != 0x80 { return 1; }
19054        if !pc32.is_null() { *pc32 = (((c & 0x0F) as u32) << 12) | (((c2 & 0x3F) as u32) << 6) | ((c3 & 0x3F) as u32); }
19055        return 3;
19056    }
19057    if c < 0xF5 {
19058        if n < 4 { return 1; }
19059        let c2 = *s.add(1);
19060        let c3 = *s.add(2);
19061        let c4 = *s.add(3);
19062        if (c2 & 0xC0) != 0x80 || (c3 & 0xC0) != 0x80 || (c4 & 0xC0) != 0x80 { return 1; }
19063        if !pc32.is_null() { *pc32 = (((c & 0x07) as u32) << 18) | (((c2 & 0x3F) as u32) << 12) | (((c3 & 0x3F) as u32) << 6) | ((c4 & 0x3F) as u32); }
19064        return 4;
19065    }
19066    1
19067}
19068
19069// ============================================================
19070// Shadow functions
19071// ============================================================
19072
19073
19074
19075
19076// ============================================================
19077// chown functions
19078// ============================================================
19079
19080#[no_mangle]
19081pub unsafe extern "C" fn chown(path: *const core::ffi::c_char, uid: u32, gid: u32) -> i32 {
19082    syscall_ret(<Arch as Syscalls>::syscall5(
19083        SYS_FCHOWNAT,
19084        AT_FDCWD as i64,
19085        path as i64,
19086        uid as i64,
19087        gid as i64,
19088        0,
19089    )) as i32
19090}
19091
19092#[no_mangle]
19093pub unsafe extern "C" fn lchown(path: *const core::ffi::c_char, uid: u32, gid: u32) -> i32 {
19094    syscall_ret(<Arch as Syscalls>::syscall5(
19095        SYS_FCHOWNAT,
19096        AT_FDCWD as i64,
19097        path as i64,
19098        uid as i64,
19099        gid as i64,
19100        0x100, // AT_SYMLINK_NOFOLLOW
19101    )) as i32
19102}
19103
19104// ============================================================
19105// chroot functions
19106// ============================================================
19107
19108#[no_mangle]
19109pub unsafe extern "C" fn chroot(path: *const core::ffi::c_char) -> i32 {
19110    syscall_ret(<Arch as Syscalls>::syscall1(SYS_CHROOT, path as i64)) as i32
19111}
19112
19113#[no_mangle]
19114pub unsafe extern "C" fn get_current_dir_name() -> *mut core::ffi::c_char {
19115    static mut BUF: [core::ffi::c_char; 4096] = [0; 4096];
19116    let r = getcwd(BUF.as_mut_ptr(), 4096);
19117    if r.is_null() { core::ptr::null_mut() } else { BUF.as_mut_ptr() }
19118}
19119
19120// ============================================================
19121// I/O functions
19122// ============================================================
19123
19124#[no_mangle]
19125pub unsafe extern "C" fn creat(pathname: *const core::ffi::c_char, mode: u32) -> i32 {
19126    // O_WRONLY | O_CREAT | O_TRUNC. The previous flags included O_EXCL.
19127    open(pathname, 1 | 0o100 | 0o1000, mode as i32)
19128}
19129
19130#[no_mangle]
19131pub unsafe extern "C" fn openat(dirfd: i32, pathname: *const core::ffi::c_char, flags: i32, mode: u32) -> i32 {
19132    syscall_ret(<Arch as Syscalls>::syscall4(SYS_OPENAT, dirfd as i64, pathname as i64, flags as i64, mode as i64)) as i32
19133}
19134
19135#[no_mangle]
19136pub unsafe extern "C" fn pwrite(fd: i32, buf: *const u8, count: usize, offset: i64) -> isize {
19137    syscall_ret(<Arch as Syscalls>::syscall4(sysno(18, 68), fd as i64, buf as i64, count as i64, offset)) as isize
19138}
19139
19140#[no_mangle]
19141pub unsafe extern "C" fn readv(fd: i32, iov: *const core::ffi::c_void, iovcnt: i32) -> isize {
19142    syscall_ret(<Arch as Syscalls>::syscall3(sysno(19, 65), fd as i64, iov as i64, iovcnt as i64)) as isize
19143}
19144
19145#[no_mangle]
19146pub unsafe extern "C" fn writev(fd: i32, iov: *const core::ffi::c_void, iovcnt: i32) -> isize {
19147    syscall_ret(<Arch as Syscalls>::syscall3(sysno(20, 66), fd as i64, iov as i64, iovcnt as i64)) as isize
19148}
19149
19150// ============================================================
19151// Lock functions
19152// ============================================================
19153
19154#[no_mangle]
19155pub unsafe extern "C" fn flock(fd: i32, operation: i32) -> i32 {
19156    syscall_ret(<Arch as Syscalls>::syscall2(SYS_FLOCK, fd as i64, operation as i64)) as i32
19157}
19158
19159#[no_mangle]
19160pub unsafe extern "C" fn lockf(fd: i32, cmd: i32, len: i64) -> i32 {
19161    let r = <Arch as Syscalls>::syscall3(SYS_FCNTL, fd as i64, if cmd == 0 { 7 } else if cmd == 1 { 6 } else { 5 } , len);
19162    if r < 0 { return -1; }
19163    0
19164}
19165
19166// ============================================================
19167// Hostname/login functions
19168// ============================================================
19169
19170#[no_mangle]
19171pub unsafe extern "C" fn getdomainname(name: *mut core::ffi::c_char, len: usize) -> i32 {
19172    if name.is_null() || len == 0 {
19173        ERRNO!() = EINVAL;
19174        return -1;
19175    }
19176    let mut uts: UtsName = core::mem::zeroed();
19177    let r = <Arch as Syscalls>::syscall1(SYS_UNAME, &mut uts as *mut UtsName as i64);
19178    if r < 0 {
19179        ERRNO!() = (-r) as c_int;
19180        return -1;
19181    }
19182    let mut n = 0usize;
19183    while n < uts.domainname.len() && uts.domainname[n] != 0 { n += 1; }
19184    if n >= len {
19185        ERRNO!() = EINVAL;
19186        return -1;
19187    }
19188    core::ptr::copy_nonoverlapping(uts.domainname.as_ptr(), name as *mut u8, n);
19189    *name.add(n) = 0;
19190    0
19191}
19192
19193#[no_mangle]
19194pub unsafe extern "C" fn setdomainname(name: *const core::ffi::c_char, len: usize) -> i32 {
19195    syscall_ret(<Arch as Syscalls>::syscall2(sysno(171, 162), name as i64, len as i64)) as i32
19196}
19197
19198#[no_mangle]
19199pub unsafe extern "C" fn sethostname(name: *const core::ffi::c_char, len: usize) -> i32 {
19200    syscall_ret(<Arch as Syscalls>::syscall2(SYS_SETHOSTNAME, name as i64, len as i64)) as i32
19201}
19202
19203#[no_mangle]
19204pub unsafe extern "C" fn getlogin_r(name: *mut core::ffi::c_char, bufsize: usize) -> i32 {
19205    // Try to get from environment
19206    let user = crate::getenv(b"USER\0".as_ptr() as *const core::ffi::c_char);
19207    if user.is_null() { return 2; }
19208    let mut i = 0;
19209    while i < bufsize - 1 && *user.add(i) != 0 {
19210        *name.add(i) = *user.add(i);
19211        i += 1;
19212    }
19213    *name.add(i) = 0;
19214    0
19215}
19216
19217#[no_mangle]
19218pub unsafe extern "C" fn cuserid(s: *mut core::ffi::c_char) -> *mut core::ffi::c_char {
19219    let user = crate::getenv(b"USER\0".as_ptr() as *const core::ffi::c_char);
19220    if user.is_null() { return core::ptr::null_mut(); }
19221    if s.is_null() {
19222        static mut BUF: [core::ffi::c_char; 32] = [0; 32];
19223        let mut i = 0;
19224        while i < 31 && *user.add(i) != 0 { BUF[i] = *user.add(i); i += 1; }
19225        BUF[i] = 0;
19226        BUF.as_mut_ptr()
19227    } else {
19228        let mut i = 0;
19229        while i < 31 && *user.add(i) != 0 { *s.add(i) = *user.add(i); i += 1; }
19230        *s.add(i) = 0;
19231        s
19232    }
19233}
19234
19235// ============================================================
19236// Group and passwd functions
19237// ============================================================
19238
19239#[repr(C)]
19240#[derive(Copy, Clone)]
19241pub struct Passwd {
19242    pub pw_name: *mut c_char,
19243    pub pw_passwd: *mut c_char,
19244    pub pw_uid: u32,
19245    pub pw_gid: u32,
19246    pub pw_gecos: *mut c_char,
19247    pub pw_dir: *mut c_char,
19248    pub pw_shell: *mut c_char,
19249}
19250
19251#[repr(C)]
19252#[derive(Copy, Clone)]
19253pub struct Group {
19254    pub gr_name: *mut c_char,
19255    pub gr_passwd: *mut c_char,
19256    pub gr_gid: u32,
19257    pub gr_mem: *mut *mut c_char,
19258}
19259
19260static mut PWD_ENT: Passwd = Passwd {
19261    pw_name: core::ptr::null_mut(), pw_passwd: core::ptr::null_mut(),
19262    pw_uid: 0, pw_gid: 0, pw_gecos: core::ptr::null_mut(),
19263    pw_dir: core::ptr::null_mut(), pw_shell: core::ptr::null_mut(),
19264};
19265static mut PWD_BUF: [c_char; 1024] = [0; 1024];
19266static mut PWD_FILE: *mut FILE = core::ptr::null_mut();
19267
19268static mut GRP_ENT: Group = Group {
19269    gr_name: core::ptr::null_mut(), gr_passwd: core::ptr::null_mut(),
19270    gr_gid: 0, gr_mem: core::ptr::null_mut(),
19271};
19272static mut GRP_BUF: [c_char; 2048] = [0; 2048];
19273static mut GRP_MEM: [*mut c_char; 32] = [core::ptr::null_mut(); 32];
19274static mut GRP_FILE: *mut FILE = core::ptr::null_mut();
19275
19276unsafe fn parse_passwd(line: *const c_char, ent: *mut Passwd, buf: *mut c_char, buflen: usize) -> bool {
19277    let mut src = line;
19278    let mut o = 0usize;
19279    let mut fields: [*mut c_char; 7] = [core::ptr::null_mut(); 7];
19280    let mut n = 0usize;
19281    while n < 7 {
19282        let start = o;
19283        while *src != 0 && *src != b':' as c_char {
19284            if o + 1 >= buflen { return false; }
19285            *buf.add(o) = *src;
19286            o += 1;
19287            src = src.add(1);
19288        }
19289        *buf.add(o) = 0;
19290        o += 1;
19291        fields[n] = buf.add(start);
19292        n += 1;
19293        if *src == 0 { break; }
19294        src = src.add(1);
19295    }
19296    if n < 7 { return false; }
19297    (*ent).pw_name = fields[0];
19298    (*ent).pw_passwd = fields[1];
19299    (*ent).pw_uid = crate::strtol(fields[2], core::ptr::null_mut(), 10) as u32;
19300    (*ent).pw_gid = crate::strtol(fields[3], core::ptr::null_mut(), 10) as u32;
19301    (*ent).pw_gecos = fields[4];
19302    (*ent).pw_dir = fields[5];
19303    (*ent).pw_shell = fields[6];
19304    true
19305}
19306
19307unsafe fn parse_group(line: *const c_char, ent: *mut Group, buf: *mut c_char, buflen: usize) -> bool {
19308    let mut src = line;
19309    let mut o = 0usize;
19310    let mut fields: [*mut c_char; 4] = [core::ptr::null_mut(); 4];
19311    let mut n = 0usize;
19312    while n < 4 {
19313        let start = o;
19314        while *src != 0 && *src != b':' as c_char {
19315            if o + 1 >= buflen { return false; }
19316            *buf.add(o) = *src;
19317            o += 1;
19318            src = src.add(1);
19319        }
19320        *buf.add(o) = 0;
19321        o += 1;
19322        fields[n] = buf.add(start);
19323        n += 1;
19324        if *src == 0 { break; }
19325        src = src.add(1);
19326    }
19327    if n < 4 { return false; }
19328    (*ent).gr_name = fields[0];
19329    (*ent).gr_passwd = fields[1];
19330    (*ent).gr_gid = crate::strtol(fields[2], core::ptr::null_mut(), 10) as u32;
19331    let mut mi = 0usize;
19332    let mut m = fields[3];
19333    if *m != 0 {
19334        loop {
19335            if mi + 1 >= GRP_MEM.len() { break; }
19336            GRP_MEM[mi] = m;
19337            mi += 1;
19338            while *m != 0 && *m != b',' as c_char { m = m.add(1); }
19339            if *m == 0 { break; }
19340            *m = 0;
19341            m = m.add(1);
19342        }
19343    }
19344    GRP_MEM[mi] = core::ptr::null_mut();
19345    (*ent).gr_mem = GRP_MEM.as_mut_ptr();
19346    true
19347}
19348
19349#[no_mangle]
19350pub unsafe extern "C" fn getpwnam(name: *const c_char) -> *mut Passwd {
19351    if name.is_null() { return core::ptr::null_mut(); }
19352    setpwent();
19353    loop {
19354        let p = getpwent();
19355        if p.is_null() { break; }
19356        if crate::strcmp((*p).pw_name as *const u8, name as *const u8) == 0 { endpwent(); return p; }
19357    }
19358    endpwent();
19359    core::ptr::null_mut()
19360}
19361
19362#[no_mangle]
19363pub unsafe extern "C" fn getpwuid(uid: u32) -> *mut Passwd {
19364    setpwent();
19365    loop {
19366        let p = getpwent();
19367        if p.is_null() { break; }
19368        if (*p).pw_uid == uid { endpwent(); return p; }
19369    }
19370    endpwent();
19371    core::ptr::null_mut()
19372}
19373
19374#[no_mangle]
19375pub unsafe extern "C" fn setpwent() {
19376    endpwent();
19377    PWD_FILE = crate::fopen(b"/etc/passwd\0".as_ptr() as *const c_char, b"r\0".as_ptr() as *const c_char);
19378}
19379
19380#[no_mangle]
19381pub unsafe extern "C" fn getpwent() -> *mut Passwd {
19382    if PWD_FILE.is_null() { setpwent(); if PWD_FILE.is_null() { return core::ptr::null_mut(); } }
19383    let mut line = [0 as c_char; 1024];
19384    loop {
19385        if crate::fgets(line.as_mut_ptr(), 1024, PWD_FILE).is_null() { return core::ptr::null_mut(); }
19386        if parse_passwd(line.as_ptr(), &raw mut PWD_ENT, PWD_BUF.as_mut_ptr(), PWD_BUF.len()) {
19387            return &raw mut PWD_ENT;
19388        }
19389    }
19390}
19391
19392#[no_mangle]
19393pub unsafe extern "C" fn endpwent() {
19394    if !PWD_FILE.is_null() { crate::fclose(PWD_FILE); PWD_FILE = core::ptr::null_mut(); }
19395}
19396
19397#[no_mangle]
19398pub unsafe extern "C" fn getpwnam_r(name: *const c_char, pwd: *mut Passwd, _buf: *mut c_char, _buflen: usize, result: *mut *mut Passwd) -> c_int {
19399    if result.is_null() { return EINVAL; }
19400    let p = getpwnam(name);
19401    if p.is_null() { *result = core::ptr::null_mut(); 0 } else { *pwd = *p; *result = pwd; 0 }
19402}
19403
19404#[no_mangle]
19405pub unsafe extern "C" fn getpwuid_r(uid: u32, pwd: *mut Passwd, _buf: *mut c_char, _buflen: usize, result: *mut *mut Passwd) -> c_int {
19406    if result.is_null() { return EINVAL; }
19407    let p = getpwuid(uid);
19408    if p.is_null() { *result = core::ptr::null_mut(); 0 } else { *pwd = *p; *result = pwd; 0 }
19409}
19410
19411#[no_mangle]
19412pub unsafe extern "C" fn fgetpwent(f: *mut FILE) -> *mut Passwd {
19413    if f.is_null() { return core::ptr::null_mut(); }
19414    let mut line = [0 as c_char; 1024];
19415    loop {
19416        if crate::fgets(line.as_mut_ptr(), 1024, f).is_null() { return core::ptr::null_mut(); }
19417        if parse_passwd(line.as_ptr(), &raw mut PWD_ENT, PWD_BUF.as_mut_ptr(), PWD_BUF.len()) {
19418            return &raw mut PWD_ENT;
19419        }
19420    }
19421}
19422
19423#[no_mangle]
19424pub unsafe extern "C" fn putpwent(f: *mut FILE, p: *const Passwd) -> c_int {
19425    if f.is_null() || p.is_null() { return -1; }
19426    crate::fprintf(f, b"%s:%s:%u:%u:%s:%s:%s\n\0".as_ptr() as *const c_char,
19427        (*p).pw_name, (*p).pw_passwd, (*p).pw_uid, (*p).pw_gid,
19428        (*p).pw_gecos, (*p).pw_dir, (*p).pw_shell)
19429}
19430
19431#[no_mangle]
19432pub unsafe extern "C" fn getgrnam(name: *const c_char) -> *mut Group {
19433    if name.is_null() { return core::ptr::null_mut(); }
19434    setgrent();
19435    loop {
19436        let g = getgrent();
19437        if g.is_null() { break; }
19438        if crate::strcmp((*g).gr_name as *const u8, name as *const u8) == 0 { endgrent(); return g; }
19439    }
19440    endgrent();
19441    core::ptr::null_mut()
19442}
19443
19444#[no_mangle]
19445pub unsafe extern "C" fn getgrgid(gid: u32) -> *mut Group {
19446    setgrent();
19447    loop {
19448        let g = getgrent();
19449        if g.is_null() { break; }
19450        if (*g).gr_gid == gid { endgrent(); return g; }
19451    }
19452    endgrent();
19453    core::ptr::null_mut()
19454}
19455
19456#[no_mangle]
19457pub unsafe extern "C" fn setgrent() {
19458    endgrent();
19459    GRP_FILE = crate::fopen(b"/etc/group\0".as_ptr() as *const c_char, b"r\0".as_ptr() as *const c_char);
19460}
19461
19462#[no_mangle]
19463pub unsafe extern "C" fn getgrent() -> *mut Group {
19464    if GRP_FILE.is_null() { setgrent(); if GRP_FILE.is_null() { return core::ptr::null_mut(); } }
19465    let mut line = [0 as c_char; 1024];
19466    loop {
19467        if crate::fgets(line.as_mut_ptr(), 1024, GRP_FILE).is_null() { return core::ptr::null_mut(); }
19468        if parse_group(line.as_ptr(), &raw mut GRP_ENT, GRP_BUF.as_mut_ptr(), GRP_BUF.len()) {
19469            return &raw mut GRP_ENT;
19470        }
19471    }
19472}
19473
19474#[no_mangle]
19475pub unsafe extern "C" fn endgrent() {
19476    if !GRP_FILE.is_null() { crate::fclose(GRP_FILE); GRP_FILE = core::ptr::null_mut(); }
19477}
19478
19479#[no_mangle]
19480pub unsafe extern "C" fn getgrnam_r(name: *const c_char, grp: *mut Group, _buf: *mut c_char, _buflen: usize, result: *mut *mut Group) -> c_int {
19481    if result.is_null() { return EINVAL; }
19482    let g = getgrnam(name);
19483    if g.is_null() { *result = core::ptr::null_mut(); 0 } else { *grp = *g; *result = grp; 0 }
19484}
19485
19486#[no_mangle]
19487pub unsafe extern "C" fn getgrgid_r(gid: u32, grp: *mut Group, _buf: *mut c_char, _buflen: usize, result: *mut *mut Group) -> c_int {
19488    if result.is_null() { return EINVAL; }
19489    let g = getgrgid(gid);
19490    if g.is_null() { *result = core::ptr::null_mut(); 0 } else { *grp = *g; *result = grp; 0 }
19491}
19492
19493#[no_mangle]
19494pub unsafe extern "C" fn fgetgrent(f: *mut FILE) -> *mut Group {
19495    if f.is_null() { return core::ptr::null_mut(); }
19496    let mut line = [0 as c_char; 1024];
19497    loop {
19498        if crate::fgets(line.as_mut_ptr(), 1024, f).is_null() { return core::ptr::null_mut(); }
19499        if parse_group(line.as_ptr(), &raw mut GRP_ENT, GRP_BUF.as_mut_ptr(), GRP_BUF.len()) {
19500            return &raw mut GRP_ENT;
19501        }
19502    }
19503}
19504
19505#[no_mangle]
19506pub unsafe extern "C" fn putgrent(f: *mut FILE, g: *const Group) -> c_int {
19507    if f.is_null() || g.is_null() { return -1; }
19508    crate::fprintf(f, b"%s:%s:%u:\0".as_ptr() as *const c_char, (*g).gr_name, (*g).gr_passwd, (*g).gr_gid);
19509    if !(*g).gr_mem.is_null() {
19510        let mut i = 0;
19511        while !(*(*g).gr_mem.add(i)).is_null() {
19512            if i > 0 { crate::fputc(b',' as c_int, f); }
19513            crate::fputs(*(*g).gr_mem.add(i), f);
19514            i += 1;
19515        }
19516    }
19517    crate::fputc(b'\n' as c_int, f);
19518    0
19519}
19520
19521// ============================================================
19522// User/group functions
19523// ============================================================
19524
19525#[no_mangle]
19526pub unsafe extern "C" fn setgroups(ngroups: usize, gidset: *const u32) -> i32 {
19527    syscall_ret(<Arch as Syscalls>::syscall2(SYS_SETGROUPS, ngroups as i64, gidset as i64)) as i32
19528}
19529
19530#[no_mangle]
19531pub unsafe extern "C" fn setresuid(ruid: u32, euid: u32, suid: u32) -> i32 {
19532    syscall_ret(<Arch as Syscalls>::syscall3(SYS_SETRESUID, ruid as i64, euid as i64, suid as i64)) as i32
19533}
19534
19535#[no_mangle]
19536pub unsafe extern "C" fn setresgid(rgid: u32, egid: u32, sgid: u32) -> i32 {
19537    syscall_ret(<Arch as Syscalls>::syscall3(SYS_SETRESGID, rgid as i64, egid as i64, sgid as i64)) as i32
19538}
19539
19540#[no_mangle]
19541pub unsafe extern "C" fn getresuid(ruid: *mut u32, euid: *mut u32, suid: *mut u32) -> i32 {
19542    syscall_ret(<Arch as Syscalls>::syscall3(SYS_GETRESUID, ruid as i64, euid as i64, suid as i64)) as i32
19543}
19544
19545#[no_mangle]
19546pub unsafe extern "C" fn getresgid(rgid: *mut u32, egid: *mut u32, sgid: *mut u32) -> i32 {
19547    syscall_ret(<Arch as Syscalls>::syscall3(SYS_GETRESGID, rgid as i64, egid as i64, sgid as i64)) as i32
19548}
19549
19550// ============================================================
19551// Socket functions
19552// ============================================================
19553
19554// musl's public struct msghdr uses 32-bit msg_iovlen/msg_controllen followed
19555// by padding on 64-bit little-endian. The kernel reads 64-bit fields, so the
19556// padding must be zero; callers (nginx, for example) do not initialize it.
19557#[repr(C)]
19558struct KernelMsgHdr {
19559    msg_name: *mut c_void,
19560    msg_namelen: u32,
19561    __pad0: u32,
19562    msg_iov: *mut c_void,
19563    msg_iovlen: u64,
19564    msg_control: *mut c_void,
19565    msg_controllen: u64,
19566    msg_flags: u32,
19567    __pad1: u32,
19568}
19569
19570#[repr(C)]
19571struct UserMsgHdr {
19572    msg_name: *mut c_void,
19573    msg_namelen: u32,
19574    msg_iov: *mut c_void,
19575    msg_iovlen: i32,
19576    __pad1: i32,
19577    msg_control: *mut c_void,
19578    msg_controllen: u32,
19579    __pad2: i32,
19580    msg_flags: i32,
19581}
19582
19583unsafe fn msg_to_kernel(msg: *const UserMsgHdr, out: &mut KernelMsgHdr) {
19584    *out = KernelMsgHdr {
19585        msg_name: (*msg).msg_name,
19586        msg_namelen: (*msg).msg_namelen,
19587        __pad0: 0,
19588        msg_iov: (*msg).msg_iov,
19589        msg_iovlen: (*msg).msg_iovlen as u32 as u64,
19590        msg_control: (*msg).msg_control,
19591        msg_controllen: (*msg).msg_controllen as u64,
19592        msg_flags: (*msg).msg_flags as u32,
19593        __pad1: 0,
19594    };
19595}
19596
19597#[no_mangle]
19598pub unsafe extern "C" fn sendmsg(fd: i32, msg: *const core::ffi::c_void, flags: i32) -> isize {
19599    if msg.is_null() {
19600        ERRNO!() = EINVAL;
19601        return -1;
19602    }
19603    let mut k: KernelMsgHdr = core::mem::zeroed();
19604    msg_to_kernel(msg as *const UserMsgHdr, &mut k);
19605    syscall_ret(<Arch as Syscalls>::syscall3(SYS_SENDMSG, fd as i64, &k as *const KernelMsgHdr as i64, flags as i64)) as isize
19606}
19607
19608#[no_mangle]
19609pub unsafe extern "C" fn recvmsg(fd: i32, msg: *mut core::ffi::c_void, flags: i32) -> isize {
19610    if msg.is_null() {
19611        ERRNO!() = EINVAL;
19612        return -1;
19613    }
19614    let user = msg as *mut UserMsgHdr;
19615    let mut k: KernelMsgHdr = core::mem::zeroed();
19616    msg_to_kernel(user, &mut k);
19617    let r = syscall_ret(<Arch as Syscalls>::syscall3(SYS_RECVMSG, fd as i64, &mut k as *mut KernelMsgHdr as i64, flags as i64));
19618    (*user).msg_flags = k.msg_flags as i32;
19619    (*user).msg_controllen = k.msg_controllen as u32;
19620    r as isize
19621}
19622
19623#[no_mangle]
19624pub unsafe extern "C" fn sendmmsg(fd: i32, msgvec: *mut core::ffi::c_void, vlen: u32, flags: i32) -> i32 {
19625    syscall_ret(<Arch as Syscalls>::syscall4(sysno(307, 269), fd as i64, msgvec as i64, vlen as i64, flags as i64)) as i32
19626}
19627
19628#[no_mangle]
19629pub unsafe extern "C" fn recvmmsg(fd: i32, msgvec: *mut core::ffi::c_void, vlen: u32, flags: i32, timeout: *mut core::ffi::c_void) -> i32 {
19630    syscall_ret(<Arch as Syscalls>::syscall5(sysno(299, 243), fd as i64, msgvec as i64, vlen as i64, flags as i64, timeout as i64)) as i32
19631}
19632
19633#[no_mangle]
19634pub unsafe extern "C" fn getpeername(fd: i32, addr: *mut core::ffi::c_void, addrlen: *mut u32) -> i32 {
19635    syscall_ret(<Arch as Syscalls>::syscall3(sysno(52, 205), fd as i64, addr as i64, addrlen as i64)) as i32
19636}
19637
19638#[no_mangle]
19639pub unsafe extern "C" fn getsockopt(fd: i32, level: i32, optname: i32, optval: *mut core::ffi::c_void, optlen: *mut u32) -> i32 {
19640    syscall_ret(<Arch as Syscalls>::syscall5(sysno(55, 209), fd as i64, level as i64, optname as i64, optval as i64, optlen as i64)) as i32
19641}
19642
19643// ============================================================
19644// stdio unlocked functions
19645// ============================================================
19646
19647#[no_mangle]
19648pub unsafe extern "C" fn ftrylockfile(file: *mut FILE) -> i32 {
19649    if file.is_null() { return EINVAL; }
19650    if (*file).lock < 0 { return 0; }
19651    let tid = sys_gettid() as c_int;
19652    let v = a_load(&raw const (*file).lock);
19653    if v == tid {
19654        (*file).lockcount += 1;
19655        return 0;
19656    }
19657    if v == 0 && a_cas(&raw mut (*file).lock, 0, tid) == 0 {
19658        (*file).lockcount = 1;
19659        return 0;
19660    }
19661    EBUSY
19662}
19663
19664#[no_mangle]
19665pub unsafe extern "C" fn getc_unlocked(file: *mut core::ffi::c_char) -> i32 {
19666    getc(file as *mut FILE)
19667}
19668
19669#[no_mangle]
19670pub unsafe extern "C" fn getchar_unlocked() -> i32 {
19671    getchar()
19672}
19673
19674#[no_mangle]
19675pub unsafe extern "C" fn putc_unlocked(c: i32, file: *mut core::ffi::c_char) -> i32 {
19676    putc(c, file as *mut FILE)
19677}
19678
19679#[no_mangle]
19680pub unsafe extern "C" fn putchar_unlocked(c: i32) -> i32 {
19681    putchar(c)
19682}
19683
19684// ============================================================
19685// getopt functions
19686// ============================================================
19687
19688#[no_mangle] pub static mut optarg: *mut core::ffi::c_char = core::ptr::null_mut();
19689#[no_mangle] pub static mut optind: i32 = 1;
19690#[no_mangle] pub static mut opterr: i32 = 1;
19691#[no_mangle] pub static mut optopt: i32 = '?' as i32;
19692#[no_mangle] pub static mut optreset: i32 = 0;
19693
19694// ============================================================
19695// Timer functions
19696// ============================================================
19697
19698#[no_mangle]
19699pub unsafe extern "C" fn getitimer(which: i32, curr_value: *mut core::ffi::c_void) -> i32 {
19700    syscall_ret(<Arch as Syscalls>::syscall2(sysno(36, 102), which as i64, curr_value as i64)) as i32
19701}
19702
19703#[no_mangle]
19704pub unsafe extern "C" fn setitimer(which: i32, new_value: *const core::ffi::c_void, old_value: *mut core::ffi::c_void) -> i32 {
19705    syscall_ret(<Arch as Syscalls>::syscall3(sysno(38, 103), which as i64, new_value as i64, old_value as i64)) as i32
19706}
19707
19708// ============================================================
19709// System info functions
19710// ============================================================
19711
19712#[no_mangle]
19713pub unsafe extern "C" fn getloadavg(loadavg: *mut f64, nelem: i32) -> i32 {
19714    let fd = crate::open(b"/proc/loadavg\0".as_ptr() as *const core::ffi::c_char, 0, 0);
19715    if fd < 0 { return -1; }
19716    let mut buf: [u8; 64] = [0; 64];
19717    let n = crate::read(fd, buf.as_mut_ptr() as *mut core::ffi::c_void, 63);
19718    crate::close(fd);
19719    if n <= 0 { return -1; }
19720    buf[n as usize] = 0;
19721    let mut i = 0;
19722    while i < nelem as usize && i < 3 {
19723        let val = crate::strtod((buf.as_ptr() as *const core::ffi::c_char).add(i * 5), core::ptr::null_mut());
19724        *loadavg.add(i) = val;
19725        i += 1;
19726    }
19727    i as i32
19728}
19729
19730#[no_mangle]
19731pub unsafe extern "C" fn get_nprocs() -> i32 {
19732    sysconf(84) as i32 // _SC_NPROCESSORS_ONLN
19733}
19734
19735#[no_mangle]
19736pub unsafe extern "C" fn get_nprocs_conf() -> i32 {
19737    sysconf(83) as i32 // _SC_NPROCESSORS_CONF
19738}
19739
19740#[no_mangle]
19741pub unsafe extern "C" fn get_avphys_pages() -> i64 {
19742    sysconf(86) // _SC_AVPHYS_PAGES
19743}
19744
19745#[no_mangle]
19746pub unsafe extern "C" fn get_phys_pages() -> i64 {
19747    sysconf(85) // _SC_PHYS_PAGES
19748}
19749
19750// ============================================================
19751// Capability functions
19752// ============================================================
19753
19754#[no_mangle]
19755pub unsafe extern "C" fn capget(header: *mut core::ffi::c_void, data: *mut core::ffi::c_void) -> i32 {
19756    syscall_ret(<Arch as Syscalls>::syscall2(SYS_CAPGET, header as i64, data as i64)) as i32
19757}
19758
19759#[no_mangle]
19760pub unsafe extern "C" fn capset(header: *const core::ffi::c_void, data: *const core::ffi::c_void) -> i32 {
19761    syscall_ret(<Arch as Syscalls>::syscall2(SYS_CAPSET, header as i64, data as i64)) as i32
19762}
19763
19764#[no_mangle]
19765pub unsafe extern "C" fn arch_prctl(code: i32, addr: u64) -> i32 {
19766    if cfg!(target_arch = "x86_64") { syscall_ret(<Arch as Syscalls>::syscall2(158, code as i64, addr as i64)) as i32 } else { ERRNO!() = 38; -1 }
19767}
19768
19769// ============================================================
19770// termios functions
19771// ============================================================
19772
19773#[no_mangle]
19774pub unsafe extern "C" fn cfgetospeed(termios_p: *const core::ffi::c_void) -> u32 {
19775    unsafe { *(termios_p as *const u32).add(1) }
19776}
19777
19778#[no_mangle]
19779pub unsafe extern "C" fn cfgetispeed(termios_p: *const core::ffi::c_void) -> u32 {
19780    unsafe { *(termios_p as *const u32).add(2) }
19781}
19782
19783#[no_mangle]
19784pub unsafe extern "C" fn cfsetospeed(termios_p: *mut core::ffi::c_void, speed: u32) -> i32 {
19785    unsafe { *(termios_p as *mut u32).add(1) = speed; }
19786    0
19787}
19788
19789#[no_mangle]
19790pub unsafe extern "C" fn cfsetispeed(termios_p: *mut core::ffi::c_void, speed: u32) -> i32 {
19791    unsafe { *(termios_p as *mut u32).add(2) = speed; }
19792    0
19793}
19794
19795#[no_mangle]
19796pub unsafe extern "C" fn cfmakeraw(termios_p: *mut core::ffi::c_void) {
19797    unsafe {
19798        let p = termios_p as *mut u32;
19799        *p.add(0) &= !0o37;
19800        *p.add(0) &= !(0o4000 | 0o2000 | 0o10000 | 0o20);
19801        *p.add(0) |= 0o60;
19802        *p.add(3) &= !0o17;
19803        *p.add(3) &= !(0o1000 | 0o40000000);
19804        *p.add(1) = 1;
19805        *p.add(2) = 0;
19806    }
19807}
19808
19809// ============================================================
19810// Resource functions
19811// ============================================================
19812
19813#[no_mangle]
19814pub unsafe extern "C" fn getrusage(who: i32, usage: *mut core::ffi::c_void) -> i32 {
19815    syscall_ret(<Arch as Syscalls>::syscall2(sysno(98, 165), who as i64, usage as i64)) as i32
19816}
19817
19818#[no_mangle]
19819pub unsafe extern "C" fn getpriority(which: i32, who: u32) -> i32 {
19820    // The kernel encodes nice as 20 - nice; musl converts it back.
19821    let r = <Arch as Syscalls>::syscall2(SYS_GETPRIORITY, which as i64, who as i64);
19822    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
19823    20 - r as i32
19824}
19825
19826#[no_mangle]
19827pub unsafe extern "C" fn setpriority(which: i32, who: u32, prio: i32) -> i32 {
19828    syscall_ret(<Arch as Syscalls>::syscall3(SYS_SETPRIORITY, which as i64, who as i64, prio as i64)) as i32
19829}
19830
19831#[no_mangle]
19832pub unsafe extern "C" fn prlimit(pid: u32, resource: i32, new_limit: *const core::ffi::c_void, old_limit: *mut core::ffi::c_void) -> i32 {
19833    syscall_ret(<Arch as Syscalls>::syscall4(sysno(302, 261), pid as i64, resource as i64, new_limit as i64, old_limit as i64)) as i32
19834}
19835
19836// ============================================================
19837// fork/clone functions
19838// ============================================================
19839
19840#[no_mangle]
19841pub unsafe extern "C" fn clone(
19842    func: usize,
19843    stack: *mut u8,
19844    flags: c_int,
19845    arg: *mut c_void,
19846    mut args: ...
19847) -> c_int {
19848    // musl-compatible signature: clone(fn, stack, flags, arg, ptid, tls, ctid).
19849    const CLONE_PIDFD_C: c_int = 0x00001000;
19850    const CLONE_VM_C: c_int = 0x00000100;
19851    const CLONE_THREAD_C: c_int = 0x00010000;
19852    const CLONE_SETTLS_C: c_int = 0x00080000;
19853    const CLONE_PARENT_SETTID_C: c_int = 0x00100000;
19854    const CLONE_CHILD_SETTID_C: c_int = 0x01000000;
19855    const CLONE_CHILD_CLEARTID_C: c_int = 0x00200000;
19856    let _ = CLONE_VM_C;
19857    if flags & (CLONE_THREAD_C | CLONE_SETTLS_C | CLONE_CHILD_CLEARTID_C) != 0
19858        || stack.is_null()
19859    {
19860        ERRNO!() = EINVAL;
19861        return -1;
19862    }
19863    let mut ptid: *mut c_int = core::ptr::null_mut();
19864    let mut tls: c_ulong = 0;
19865    let mut ctid: *mut c_int = core::ptr::null_mut();
19866    if flags & (CLONE_PIDFD_C | CLONE_PARENT_SETTID_C | CLONE_CHILD_SETTID_C) != 0 {
19867        ptid = args.next_arg::<*mut c_int>();
19868    }
19869    if flags & CLONE_CHILD_SETTID_C != 0 {
19870        tls = args.next_arg::<c_ulong>();
19871        ctid = args.next_arg::<*mut c_int>();
19872    }
19873    let r = __rc_clone(func, stack, flags as c_ulong, arg, ptid, tls, ctid);
19874    if r < 0 {
19875        ERRNO!() = (-r) as c_int;
19876        -1
19877    } else {
19878        r as c_int
19879    }
19880}
19881
19882#[no_mangle]
19883pub unsafe extern "C" fn _Fork() -> i32 {
19884    crate::fork()
19885}
19886
19887#[no_mangle]
19888pub unsafe extern "C" fn forkpty(amaster: *mut i32, name: *mut core::ffi::c_char, termp: *const core::ffi::c_void, winp: *const core::ffi::c_void) -> i32 {
19889    let mut master: i32 = 0;
19890    let mut slave: i32 = 0;
19891    if openpty(&mut master, &mut slave, name, termp, winp) < 0 { return -1; }
19892    let pid = crate::fork();
19893    if pid < 0 { crate::close(master); crate::close(slave); return -1; }
19894    if pid == 0 {
19895        crate::close(master);
19896        crate::setsid();
19897        crate::dup2(slave, 0);
19898        crate::dup2(slave, 1);
19899        crate::dup2(slave, 2);
19900    } else {
19901        crate::close(slave);
19902        if !amaster.is_null() { *amaster = master; }
19903    }
19904    pid
19905}
19906
19907#[no_mangle]
19908pub unsafe extern "C" fn openpty(amaster: *mut i32, aslave: *mut i32, name: *mut core::ffi::c_char, _termp: *const core::ffi::c_void, _winp: *const core::ffi::c_void) -> i32 {
19909    let master = crate::open(b"/dev/ptmx\0".as_ptr() as *const core::ffi::c_char, 0o2000, 0);
19910    if master < 0 { return -1; }
19911    grantpt(master);
19912    unlockpt(master);
19913    let slave_name = ptsname(master);
19914    if slave_name.is_null() { crate::close(master); return -1; }
19915    let slave = crate::open(slave_name, 0o2000, 0);
19916    if slave < 0 { crate::close(master); return -1; }
19917    if !amaster.is_null() { *amaster = master; }
19918    if !aslave.is_null() { *aslave = slave; }
19919    if !name.is_null() { crate::strcpy(name as *mut u8, slave_name as *const u8); }
19920    0
19921}
19922
19923#[no_mangle]
19924pub unsafe extern "C" fn login_tty(fd: i32) -> i32 {
19925    crate::setsid();
19926    crate::dup2(fd, 0);
19927    crate::dup2(fd, 1);
19928    crate::dup2(fd, 2);
19929    0
19930}
19931
19932// ============================================================
19933// dl functions
19934// ============================================================
19935
19936#[no_mangle]
19937pub unsafe extern "C" fn __dls2b() {}
19938#[no_mangle]
19939pub unsafe extern "C" fn __dls3() {}
19940#[no_mangle]
19941pub unsafe extern "C" fn _dlstart() {}
19942
19943// ============================================================
19944// string functions
19945// ============================================================
19946
19947#[no_mangle]
19948pub unsafe extern "C" fn index(s: *const core::ffi::c_char, c: i32) -> *mut core::ffi::c_char {
19949    let mut i = 0;
19950    loop {
19951        let ch = *s.add(i);
19952        if ch == c as core::ffi::c_char { return s.add(i) as *mut core::ffi::c_char; }
19953        if ch == 0 { return core::ptr::null_mut(); }
19954        i += 1;
19955    }
19956}
19957
19958#[no_mangle]
19959pub unsafe extern "C" fn rindex(s: *const core::ffi::c_char, c: i32) -> *mut core::ffi::c_char {
19960    let mut last = core::ptr::null_mut();
19961    let mut i = 0;
19962    loop {
19963        let ch = *s.add(i);
19964        if ch == 0 { return last; }
19965        if ch == c as core::ffi::c_char { last = s.add(i) as *mut core::ffi::c_char; }
19966        i += 1;
19967    }
19968}
19969
19970// ============================================================
19971// intmax functions
19972// ============================================================
19973
19974#[no_mangle]
19975pub unsafe extern "C" fn imaxabs(j: i64) -> i64 {
19976    j.abs()
19977}
19978
19979#[no_mangle]
19980pub unsafe extern "C" fn imaxdiv(numer: i64, denom: i64) -> (i64, i64) {
19981    (numer / denom, numer % denom)
19982}
19983
19984// ============================================================
19985// herror functions
19986// ============================================================
19987
19988#[no_mangle]
19989pub static mut h_errno: i32 = 0;
19990
19991#[no_mangle]
19992pub unsafe extern "C" fn __h_errno_location() -> *mut i32 {
19993    &mut h_errno
19994}
19995
19996#[no_mangle]
19997pub unsafe extern "C" fn herror(s: *const core::ffi::c_char) {
19998    if !s.is_null() { crate::write(2, s as *const core::ffi::c_void, crate::strlen(s)); }
19999    crate::write(2, b": ".as_ptr() as *const core::ffi::c_void, 2);
20000    crate::write(2, hstrerror(*__h_errno_location()) as *const core::ffi::c_void, crate::strlen(hstrerror(*__h_errno_location())));
20001    crate::write(2, b"\n".as_ptr() as *const core::ffi::c_void, 1);
20002}
20003
20004#[no_mangle]
20005pub unsafe extern "C" fn hstrerror(_err: i32) -> *const core::ffi::c_char {
20006    b"Unknown error\0".as_ptr() as *const core::ffi::c_char
20007}
20008
20009// ============================================================
20010// gettid function
20011// ============================================================
20012
20013#[no_mangle]
20014pub unsafe extern "C" fn gettid() -> i32 {
20015    syscall_ret(<Arch as Syscalls>::syscall0(SYS_GETTID)) as i32
20016}
20017
20018// ============================================================
20019// ioctl functions
20020// ============================================================
20021
20022#[no_mangle]
20023pub unsafe extern "C" fn ioctl(fd: i32, request: u32, mut args: ...) -> i32 {
20024    let arg = args.next_arg::<i64>();
20025    syscall_ret(<Arch as Syscalls>::syscall3(SYS_IOCTL, fd as i64, request as i64, arg)) as i32
20026}
20027
20028#[no_mangle]
20029pub unsafe extern "C" fn ioperm(from: u64, num: u64, turn_on: i32) -> i32 {
20030    #[cfg(target_arch = "x86_64")]
20031    { syscall_ret(<Arch as Syscalls>::syscall3(SYS_IOPERM, from as i64, num as i64, turn_on as i64)) as i32 }
20032    #[cfg(not(target_arch = "x86_64"))]
20033    {
20034        let _ = (from, num, turn_on);
20035        ERRNO!() = ENOSYS_VAL;
20036        -1
20037    }
20038}
20039
20040#[no_mangle]
20041pub unsafe extern "C" fn iopl(level: i32) -> i32 {
20042    #[cfg(target_arch = "x86_64")]
20043    { syscall_ret(<Arch as Syscalls>::syscall1(SYS_IOPL, level as i64)) as i32 }
20044    #[cfg(not(target_arch = "x86_64"))]
20045    {
20046        let _ = level;
20047        ERRNO!() = ENOSYS_VAL;
20048        -1
20049    }
20050}
20051
20052// ============================================================
20053// xattr functions
20054// ============================================================
20055
20056#[no_mangle]
20057pub unsafe extern "C" fn getxattr(path: *const core::ffi::c_char, name: *const core::ffi::c_char, value: *mut u8, size: usize) -> isize {
20058    syscall_ret(<Arch as Syscalls>::syscall4(sysno(191, 8), path as i64, name as i64, value as i64, size as i64)) as isize
20059}
20060
20061#[no_mangle]
20062pub unsafe extern "C" fn lgetxattr(path: *const core::ffi::c_char, name: *const core::ffi::c_char, value: *mut u8, size: usize) -> isize {
20063    syscall_ret(<Arch as Syscalls>::syscall4(sysno(192, 9), path as i64, name as i64, value as i64, size as i64)) as isize
20064}
20065
20066#[no_mangle]
20067pub unsafe extern "C" fn fgetxattr(fd: i32, name: *const core::ffi::c_char, value: *mut u8, size: usize) -> isize {
20068    syscall_ret(<Arch as Syscalls>::syscall4(sysno(193, 10), fd as i64, name as i64, value as i64, size as i64)) as isize
20069}
20070
20071#[no_mangle]
20072pub unsafe extern "C" fn listxattr(path: *const core::ffi::c_char, list: *mut u8, size: usize) -> isize {
20073    syscall_ret(<Arch as Syscalls>::syscall3(sysno(194, 11), path as i64, list as i64, size as i64)) as isize
20074}
20075
20076#[no_mangle]
20077pub unsafe extern "C" fn llistxattr(path: *const core::ffi::c_char, list: *mut u8, size: usize) -> isize {
20078    syscall_ret(<Arch as Syscalls>::syscall3(sysno(195, 12), path as i64, list as i64, size as i64)) as isize
20079}
20080
20081#[no_mangle]
20082pub unsafe extern "C" fn flistxattr(fd: i32, list: *mut u8, size: usize) -> isize {
20083    syscall_ret(<Arch as Syscalls>::syscall3(sysno(196, 13), fd as i64, list as i64, size as i64)) as isize
20084}
20085
20086#[no_mangle]
20087pub unsafe extern "C" fn setxattr(path: *const core::ffi::c_char, name: *const core::ffi::c_char, value: *const u8, size: usize, flags: i32) -> i32 {
20088    syscall_ret(<Arch as Syscalls>::syscall5(sysno(188, 5), path as i64, name as i64, value as i64, size as i64, flags as i64)) as i32
20089}
20090
20091#[no_mangle]
20092pub unsafe extern "C" fn lsetxattr(path: *const core::ffi::c_char, name: *const core::ffi::c_char, value: *const u8, size: usize, flags: i32) -> i32 {
20093    syscall_ret(<Arch as Syscalls>::syscall5(sysno(189, 6), path as i64, name as i64, value as i64, size as i64, flags as i64)) as i32
20094}
20095
20096#[no_mangle]
20097pub unsafe extern "C" fn fsetxattr(fd: i32, name: *const core::ffi::c_char, value: *const u8, size: usize, flags: i32) -> i32 {
20098    syscall_ret(<Arch as Syscalls>::syscall5(sysno(190, 7), fd as i64, name as i64, value as i64, size as i64, flags as i64)) as i32
20099}
20100
20101#[no_mangle]
20102pub unsafe extern "C" fn removexattr(path: *const core::ffi::c_char, name: *const core::ffi::c_char) -> i32 {
20103    syscall_ret(<Arch as Syscalls>::syscall2(sysno(197, 14), path as i64, name as i64)) as i32
20104}
20105
20106#[no_mangle]
20107pub unsafe extern "C" fn lremovexattr(path: *const core::ffi::c_char, name: *const core::ffi::c_char) -> i32 {
20108    syscall_ret(<Arch as Syscalls>::syscall2(sysno(198, 15), path as i64, name as i64)) as i32
20109}
20110
20111#[no_mangle]
20112pub unsafe extern "C" fn fremovexattr(fd: i32, name: *const core::ffi::c_char) -> i32 {
20113    syscall_ret(<Arch as Syscalls>::syscall2(sysno(199, 16), fd as i64, name as i64)) as i32
20114}
20115
20116// ============================================================
20117// adjtime functions
20118// ============================================================
20119
20120#[no_mangle]
20121pub unsafe extern "C" fn adjtime(_delta: *const core::ffi::c_void, olddelta: *mut core::ffi::c_void) -> i32 {
20122    syscall_ret(<Arch as Syscalls>::syscall1(SYS_ADJTIMEX, olddelta as i64)) as i32
20123}
20124
20125#[no_mangle]
20126pub unsafe extern "C" fn adjtimex(buf: *mut core::ffi::c_void) -> i32 {
20127    syscall_ret(<Arch as Syscalls>::syscall1(SYS_ADJTIMEX, buf as i64)) as i32
20128}
20129
20130// ============================================================
20131// pty functions
20132// ============================================================
20133
20134// Linux devpts ioctls (asm-generic/ioctls.h): TIOCGPTN = _IOR('T', 0x30, int),
20135// TIOCSPTLCK = _IOW('T', 0x31, int). The old 0x541x values are the window-size
20136// and process-group requests, which both return the wrong data and write more
20137// bytes than the int the caller supplied.
20138const TIOCGPTN: i64 = 0x80045430;
20139const TIOCSPTLCK: i64 = 0x40045431;
20140
20141#[no_mangle]
20142pub unsafe extern "C" fn grantpt(_fd: i32) -> i32 {
20143    // devpts creates the slave already owned by the caller.
20144    0
20145}
20146
20147#[no_mangle]
20148pub unsafe extern "C" fn unlockpt(fd: i32) -> i32 {
20149    let mut unlock: i32 = 0;
20150    let r = <Arch as Syscalls>::syscall3(
20151        SYS_IOCTL,
20152        fd as i64,
20153        TIOCSPTLCK,
20154        &mut unlock as *mut i32 as i64,
20155    );
20156    if r < 0 { -1 } else { 0 }
20157}
20158
20159#[no_mangle]
20160pub unsafe extern "C" fn ptsname(fd: i32) -> *mut core::ffi::c_char {
20161    static mut BUF: [core::ffi::c_char; 32] = [0; 32];
20162    let mut pty_nr: i32 = 0;
20163    if <Arch as Syscalls>::syscall3(SYS_IOCTL, fd as i64, TIOCGPTN, &mut pty_nr as *mut i32 as i64) < 0 {
20164        return core::ptr::null_mut();
20165    }
20166    // Format /dev/pts/N
20167    BUF[0] = '/' as c_char; BUF[1] = 'd' as c_char; BUF[2] = 'e' as c_char; BUF[3] = 'v' as c_char;
20168    BUF[4] = '/' as c_char; BUF[5] = 'p' as c_char; BUF[6] = 't' as c_char; BUF[7] = 's' as c_char;
20169    BUF[8] = '/' as c_char;
20170    let mut n = pty_nr as usize;
20171    let mut i = 9;
20172    if n == 0 { BUF[i] = '0' as c_char; i += 1; }
20173    while n > 0 && i < 31 {
20174        BUF[i] = (b'0' + (n % 10) as u8) as c_char;
20175        n /= 10;
20176        i += 1;
20177    }
20178    BUF[i] = 0;
20179    BUF.as_mut_ptr()
20180}
20181
20182// ============================================================
20183// fpclassify functions
20184// ============================================================
20185
20186#[no_mangle]
20187pub unsafe extern "C" fn finite(x: f64) -> i32 {
20188    (!x.is_nan() && !x.is_infinite()) as i32
20189}
20190
20191#[no_mangle]
20192pub unsafe extern "C" fn finitef(x: f32) -> i32 {
20193    (!x.is_nan() && !x.is_infinite()) as i32
20194}
20195
20196#[no_mangle]
20197pub unsafe extern "C" fn __fpclassifyl(x: f64) -> i32 {
20198    __fpclassify(x)
20199}
20200
20201#[no_mangle]
20202pub unsafe extern "C" fn __signbitl(x: f64) -> i32 {
20203    __signbit(x)
20204}
20205
20206// ============================================================
20207// ftw functions
20208// ============================================================
20209
20210// ============================================================
20211// Bessel functions
20212// ============================================================
20213
20214#[no_mangle]
20215pub unsafe extern "C" fn j0(x: f64) -> f64 {
20216    j0_impl(x)
20217}
20218
20219#[no_mangle]
20220pub unsafe extern "C" fn j0f(x: f32) -> f32 {
20221    libm::j0f(x)
20222}
20223
20224#[no_mangle]
20225pub unsafe extern "C" fn j1(x: f64) -> f64 {
20226    libm::j1(x)
20227}
20228
20229#[no_mangle]
20230pub unsafe extern "C" fn j1f(x: f32) -> f32 {
20231    libm::j1f(x)
20232}
20233
20234#[no_mangle]
20235pub unsafe extern "C" fn jn(n: i32, x: f64) -> f64 {
20236    libm::jn(n, x)
20237}
20238
20239#[no_mangle]
20240pub unsafe extern "C" fn jnf(n: i32, x: f32) -> f32 {
20241    libm::jnf(n, x)
20242}
20243
20244#[no_mangle]
20245pub unsafe extern "C" fn y0(x: f64) -> f64 {
20246    y0_impl(x)
20247}
20248
20249#[no_mangle]
20250pub unsafe extern "C" fn y0f(x: f32) -> f32 {
20251    if x.is_nan() { return x; }
20252    if x < 0.0 { return __math_invalidf(x); }
20253    if x == 0.0 { feraiseexcept(FE_DIVBYZERO); return f32::NEG_INFINITY; }
20254    libm::y0f(x)
20255}
20256
20257#[no_mangle]
20258pub unsafe extern "C" fn y1(x: f64) -> f64 {
20259    if x.is_nan() { return x; }
20260    if x < 0.0 { return __math_invalid(x); }
20261    if x == 0.0 { feraiseexcept(FE_DIVBYZERO); return f64::NEG_INFINITY; }
20262    libm::y1(x)
20263}
20264
20265#[no_mangle]
20266pub unsafe extern "C" fn y1f(x: f32) -> f32 {
20267    if x.is_nan() { return x; }
20268    if x < 0.0 { return __math_invalidf(x); }
20269    if x == 0.0 { feraiseexcept(FE_DIVBYZERO); return f32::NEG_INFINITY; }
20270    libm::y1f(x)
20271}
20272
20273#[no_mangle]
20274pub unsafe extern "C" fn yn(n: i32, x: f64) -> f64 {
20275    if x.is_nan() { return x; }
20276    if x < 0.0 { return __math_invalid(x); }
20277    if x == 0.0 {
20278        feraiseexcept(FE_DIVBYZERO);
20279        if n >= 0 { return f64::NEG_INFINITY; }
20280    }
20281    libm::yn(n, x)
20282}
20283
20284#[no_mangle]
20285pub unsafe extern "C" fn ynf(n: i32, x: f32) -> f32 {
20286    if x.is_nan() { return x; }
20287    if x < 0.0 { return __math_invalidf(x); }
20288    if x == 0.0 {
20289        feraiseexcept(FE_DIVBYZERO);
20290        if n >= 0 { return f32::NEG_INFINITY; }
20291    }
20292    libm::ynf(n, x)
20293}
20294
20295// ============================================================
20296// ctype locale functions
20297// ============================================================
20298
20299#[no_mangle]
20300pub unsafe extern "C" fn __ctype_b_loc() -> *mut *const u16 {
20301    static mut TABLE: *const u16 = core::ptr::null();
20302    &mut TABLE
20303}
20304
20305#[no_mangle]
20306pub unsafe extern "C" fn __ctype_tolower_loc() -> *mut *const i32 {
20307    static mut TABLE: *const i32 = core::ptr::null();
20308    &mut TABLE
20309}
20310
20311#[no_mangle]
20312pub unsafe extern "C" fn __ctype_toupper_loc() -> *mut *const i32 {
20313    static mut TABLE: *const i32 = core::ptr::null();
20314    &mut TABLE
20315}
20316
20317#[no_mangle]
20318pub unsafe extern "C" fn __isalnum_l(c: i32, _loc: *mut core::ffi::c_void) -> i32 {
20319    (c >= '0' as i32 && c <= '9' as i32 || c >= 'A' as i32 && c <= 'Z' as i32 || c >= 'a' as i32 && c <= 'z' as i32) as i32
20320}
20321
20322#[no_mangle]
20323pub unsafe extern "C" fn __isalpha_l(c: i32, _loc: *mut core::ffi::c_void) -> i32 {
20324    (c >= 'A' as i32 && c <= 'Z' as i32 || c >= 'a' as i32 && c <= 'z' as i32) as i32
20325}
20326
20327#[no_mangle]
20328pub unsafe extern "C" fn __isblank_l(c: i32, _loc: *mut core::ffi::c_void) -> i32 {
20329    (c == ' ' as i32 || c == '\t' as i32) as i32
20330}
20331
20332#[no_mangle]
20333pub unsafe extern "C" fn __iscntrl_l(c: i32, _loc: *mut core::ffi::c_void) -> i32 {
20334    (c < 0x20 || c == 0x7f) as i32
20335}
20336
20337#[no_mangle]
20338pub unsafe extern "C" fn __isdigit_l(c: i32, _loc: *mut core::ffi::c_void) -> i32 {
20339    (c >= '0' as i32 && c <= '9' as i32) as i32
20340}
20341
20342#[no_mangle]
20343pub unsafe extern "C" fn __isgraph_l(c: i32, _loc: *mut core::ffi::c_void) -> i32 {
20344    (c > 0x20 && c < 0x7f) as i32
20345}
20346
20347#[no_mangle]
20348pub unsafe extern "C" fn __islower_l(c: i32, _loc: *mut core::ffi::c_void) -> i32 {
20349    (c >= 'a' as i32 && c <= 'z' as i32) as i32
20350}
20351
20352#[no_mangle]
20353pub unsafe extern "C" fn __isprint_l(c: i32, _loc: *mut core::ffi::c_void) -> i32 {
20354    (c >= 0x20 && c < 0x7f) as i32
20355}
20356
20357#[no_mangle]
20358pub unsafe extern "C" fn __ispunct_l(c: i32, _loc: *mut core::ffi::c_void) -> i32 {
20359    (c >= 0x21 && c <= 0x2f || c >= 0x3a && c <= 0x40 || c >= 0x5b && c <= 0x60 || c >= 0x7b && c <= 0x7e) as i32
20360}
20361
20362#[no_mangle]
20363pub unsafe extern "C" fn __isspace_l(c: i32, _loc: *mut core::ffi::c_void) -> i32 {
20364    (c == ' ' as i32 || c >= 0x09 && c <= 0x0d) as i32
20365}
20366
20367#[no_mangle]
20368pub unsafe extern "C" fn __isupper_l(c: i32, _loc: *mut core::ffi::c_void) -> i32 {
20369    (c >= 'A' as i32 && c <= 'Z' as i32) as i32
20370}
20371
20372#[no_mangle]
20373pub unsafe extern "C" fn __isxdigit_l(c: i32, _loc: *mut core::ffi::c_void) -> i32 {
20374    (c >= '0' as i32 && c <= '9' as i32 || c >= 'A' as i32 && c <= 'F' as i32 || c >= 'a' as i32 && c <= 'f' as i32) as i32
20375}
20376
20377// ============================================================
20378// wide char locale functions
20379// ============================================================
20380
20381#[no_mangle]
20382pub unsafe extern "C" fn __iswalnum_l(c: u32, _loc: *mut core::ffi::c_void) -> i32 {
20383    (c >= '0' as u32 && c <= '9' as u32 || c >= 'A' as u32 && c <= 'Z' as u32 || c >= 'a' as u32 && c <= 'z' as u32) as i32
20384}
20385
20386#[no_mangle]
20387pub unsafe extern "C" fn __iswalpha_l(c: u32, _loc: *mut core::ffi::c_void) -> i32 {
20388    (c >= 'A' as u32 && c <= 'Z' as u32 || c >= 'a' as u32 && c <= 'z' as u32) as i32
20389}
20390
20391#[no_mangle]
20392pub unsafe extern "C" fn __iswblank_l(c: u32, _loc: *mut core::ffi::c_void) -> i32 {
20393    (c == ' ' as u32 || c == '\t' as u32) as i32
20394}
20395
20396#[no_mangle]
20397pub unsafe extern "C" fn __iswcntrl_l(c: u32, _loc: *mut core::ffi::c_void) -> i32 {
20398    (c < 0x20 || c == 0x7f) as i32
20399}
20400
20401#[no_mangle]
20402pub unsafe extern "C" fn __iswdigit_l(c: u32, _loc: *mut core::ffi::c_void) -> i32 {
20403    (c >= '0' as u32 && c <= '9' as u32) as i32
20404}
20405
20406#[no_mangle]
20407pub unsafe extern "C" fn __iswgraph_l(c: u32, _loc: *mut core::ffi::c_void) -> i32 {
20408    (c > 0x20 && c < 0x7f) as i32
20409}
20410
20411#[no_mangle]
20412pub unsafe extern "C" fn __iswlower_l(c: u32, _loc: *mut core::ffi::c_void) -> i32 {
20413    (c >= 'a' as u32 && c <= 'z' as u32) as i32
20414}
20415
20416#[no_mangle]
20417pub unsafe extern "C" fn __iswprint_l(c: u32, _loc: *mut core::ffi::c_void) -> i32 {
20418    (c >= 0x20 && c < 0x7f) as i32
20419}
20420
20421#[no_mangle]
20422pub unsafe extern "C" fn __iswpunct_l(c: u32, _loc: *mut core::ffi::c_void) -> i32 {
20423    (c >= 0x21 && c <= 0x2f || c >= 0x3a && c <= 0x40 || c >= 0x5b && c <= 0x60 || c >= 0x7b && c <= 0x7e) as i32
20424}
20425
20426#[no_mangle]
20427pub unsafe extern "C" fn __iswspace_l(c: u32, _loc: *mut core::ffi::c_void) -> i32 {
20428    (c == ' ' as u32 || c >= 0x09 && c <= 0x0d) as i32
20429}
20430
20431#[no_mangle]
20432pub unsafe extern "C" fn __iswupper_l(c: u32, _loc: *mut core::ffi::c_void) -> i32 {
20433    (c >= 'A' as u32 && c <= 'Z' as u32) as i32
20434}
20435
20436#[no_mangle]
20437pub unsafe extern "C" fn __iswxdigit_l(c: u32, _loc: *mut core::ffi::c_void) -> i32 {
20438    (c >= '0' as u32 && c <= '9' as u32 || c >= 'A' as u32 && c <= 'F' as u32 || c >= 'a' as u32 && c <= 'f' as u32) as i32
20439}
20440
20441#[no_mangle]
20442pub unsafe extern "C" fn __iswctype_l(c: u32, ctype: u32, _loc: *mut core::ffi::c_void) -> i32 {
20443    crate::iswctype(c as wint_t, ctype as wctype_t)
20444}
20445
20446#[no_mangle]
20447pub unsafe extern "C" fn __towlower_l(c: u32, _loc: *mut core::ffi::c_void) -> u32 {
20448    crate::towlower(c)
20449}
20450
20451#[no_mangle]
20452pub unsafe extern "C" fn __towupper_l(c: u32, _loc: *mut core::ffi::c_void) -> u32 {
20453    crate::towupper(c)
20454}
20455
20456#[no_mangle]
20457pub unsafe extern "C" fn __towctrans_l(c: u32, _trans: u32, _loc: *mut core::ffi::c_void) -> u32 {
20458    c
20459}
20460
20461// ============================================================
20462// _r functions
20463// ============================================================
20464
20465// ============================================================
20466// stdio internal functions
20467// ============================================================
20468
20469// ============================================================
20470// Misc functions
20471// ============================================================
20472
20473#[no_mangle]
20474pub unsafe extern "C" fn __assert_fail(expr: *const core::ffi::c_char, file: *const core::ffi::c_char, line: u32, func: *const core::ffi::c_char) {
20475    crate::write(2, b"Assertion failed: ".as_ptr() as *const core::ffi::c_void, 18);
20476    if !expr.is_null() { crate::write(2, expr as *const core::ffi::c_void, crate::strlen(expr)); }
20477    crate::write(2, b" (".as_ptr() as *const core::ffi::c_void, 2);
20478    if !file.is_null() { crate::write(2, file as *const core::ffi::c_void, crate::strlen(file)); }
20479    crate::write(2, b": ".as_ptr() as *const core::ffi::c_void, 2);
20480    let mut n = line as usize;
20481    let mut digits: [u8; 32] = [0; 32];
20482    let mut di = 0;
20483    if n == 0 { digits[0] = 0; di = 1; }
20484    while n > 0 && di < 32 { digits[di] = (n % 10) as u8; n /= 10; di += 1; }
20485    while di > 0 { di -= 1; let c = (b'0' + digits[di]) as c_char; crate::write(2, &c as *const c_char as *const core::ffi::c_void, 1); }
20486    crate::write(2, b")".as_ptr() as *const core::ffi::c_void, 1);
20487    if !func.is_null() { crate::write(2, func as *const core::ffi::c_void, crate::strlen(func)); }
20488    crate::write(2, b"\n".as_ptr() as *const core::ffi::c_void, 1);
20489    crate::abort()
20490}
20491
20492#[no_mangle]
20493pub unsafe extern "C" fn at_quick_exit(_func: Option<unsafe extern "C" fn()>) -> i32 {
20494    // Register for quick exit - simplified
20495    0
20496}
20497
20498#[no_mangle]
20499pub unsafe extern "C" fn brk(addr: *mut u8) -> i32 {
20500    syscall_ret(<Arch as Syscalls>::syscall1(sysno(12, 214), addr as i64)) as i32
20501}
20502
20503#[no_mangle]
20504pub unsafe extern "C" fn copy_file_range(fd_in: i32, off_in: *mut i64, fd_out: i32, off_out: *mut i64, len: usize, flags: u32) -> isize {
20505    syscall_ret(<Arch as Syscalls>::syscall6(SYS_COPY_FILE_RANGE, fd_in as i64, off_in as i64, fd_out as i64, off_out as i64, len as i64, flags as i64)) as isize
20506}
20507
20508#[no_mangle]
20509pub unsafe extern "C" fn ctermid(s: *mut core::ffi::c_char) -> *mut core::ffi::c_char {
20510    static mut BUF: [core::ffi::c_char; 20] = [0; 20];
20511    let dst = if s.is_null() { BUF.as_mut_ptr() } else { s };
20512    let src = b"/dev/tty\0".as_ptr() as *const core::ffi::c_char;
20513    let mut i = 0;
20514    while i < 9 { *dst.add(i) = *src.add(i); i += 1; }
20515    dst
20516}
20517
20518#[no_mangle]
20519pub unsafe extern "C" fn delete_module(name: *const core::ffi::c_char, flags: u32) -> i32 {
20520    syscall_ret(<Arch as Syscalls>::syscall2(SYS_DELETE_MODULE, name as i64, flags as i64)) as i32
20521}
20522
20523
20524
20525
20526
20527#[no_mangle]
20528pub unsafe extern "C" fn __duplocale(loc: *mut core::ffi::c_void) -> *mut core::ffi::c_void {
20529    loc
20530}
20531
20532#[no_mangle]
20533pub unsafe extern "C" fn euidaccess(pathname: *const core::ffi::c_char, mode: i32) -> i32 {
20534    faccessat(AT_FDCWD, pathname, mode, 0x200)
20535}
20536
20537#[no_mangle]
20538pub unsafe extern "C" fn fgetln(f: *mut core::ffi::c_void, lenp: *mut usize) -> *mut core::ffi::c_char {
20539    static mut BUF: [core::ffi::c_char; 4096] = [0; 4096];
20540    let mut i = 0;
20541    while i < 4095 {
20542        let c = getc(f as *mut FILE);
20543        if c < 0 { break; }
20544        BUF[i] = c as c_char;
20545        i += 1;
20546        if c == '\n' as i32 { break; }
20547    }
20548    BUF[i] = 0;
20549    if !lenp.is_null() { *lenp = i; }
20550    if i == 0 { core::ptr::null_mut() } else { BUF.as_mut_ptr() }
20551}
20552
20553#[no_mangle]
20554pub unsafe extern "C" fn fgetws(ws: *mut u32, n: i32, f: *mut core::ffi::c_void) -> *mut u32 {
20555    let mut i = 0;
20556    while i < n - 1 {
20557        let c = getwc(f as *mut core::ffi::c_void);
20558        if c == 0 { break; }
20559        *ws.add(i as usize) = c;
20560        i += 1;
20561        if c == '\n' as u32 { break; }
20562    }
20563    *ws.add(i as usize) = 0;
20564    if i == 0 { core::ptr::null_mut() } else { ws }
20565}
20566
20567#[no_mangle]
20568pub unsafe extern "C" fn fmtmsg(_classification: i64, label: *const core::ffi::c_char, _severity: i32, text: *const core::ffi::c_char, _action: *const core::ffi::c_char, _tag: *const core::ffi::c_char) -> i32 {
20569    if !label.is_null() { crate::write(2, label as *const core::ffi::c_void, crate::strlen(label)); }
20570    crate::write(2, b": ".as_ptr() as *const core::ffi::c_void, 2);
20571    if !text.is_null() { crate::write(2, text as *const core::ffi::c_void, crate::strlen(text)); }
20572    crate::write(2, b"\n".as_ptr() as *const core::ffi::c_void, 1);
20573    0
20574}
20575
20576#[no_mangle]
20577pub unsafe extern "C" fn ftime(tp: *mut core::ffi::c_void) -> i32 {
20578    let mut tv: timeval = core::mem::zeroed();
20579    if gettimeofday(&mut tv, core::ptr::null_mut()) == 0 {
20580        let tb = tp as *mut Timeb;
20581        (*tb).time = tv.tv_sec;
20582        (*tb).millitm = (tv.tv_usec / 1000) as u16;
20583        (*tb).timezone = 0;
20584        (*tb).dstflag = 0;
20585        0
20586    } else {
20587        -1
20588    }
20589}
20590
20591#[no_mangle]
20592pub unsafe extern "C" fn getdents(fd: i32, dirp: *mut u8, count: usize) -> isize {
20593    syscall_ret(<Arch as Syscalls>::syscall3(SYS_GETDENTS64, fd as i64, dirp as i64, count as i64)) as isize
20594}
20595
20596#[no_mangle]
20597pub unsafe extern "C" fn gethostid() -> i64 {
20598    // Read from /proc/sys/kernel/random/boot_id or similar
20599    let fd = crate::open(b"/proc/sys/kernel/random/boot_id\0".as_ptr() as *const core::ffi::c_char, 0, 0);
20600    if fd < 0 { return 0; }
20601    let mut buf: [u8; 64] = [0; 64];
20602    let n = crate::read(fd, buf.as_mut_ptr() as *mut core::ffi::c_void, 63);
20603    crate::close(fd);
20604    if n <= 0 { return 0; }
20605    buf[n as usize] = 0;
20606    crate::strtol(buf.as_ptr() as *const core::ffi::c_char, core::ptr::null_mut(), 16)
20607}
20608
20609#[no_mangle]
20610pub unsafe extern "C" fn getpass(prompt: *const core::ffi::c_char) -> *mut core::ffi::c_char {
20611    static mut BUF: [core::ffi::c_char; 128] = [0; 128];
20612    // Write prompt
20613    crate::write(2, prompt as *const core::ffi::c_void, crate::strlen(prompt));
20614    // Read password (simplified - no echo disable)
20615    let n = crate::read(0, BUF.as_mut_ptr() as *mut core::ffi::c_void, 127);
20616    if n > 0 {
20617        let end = if BUF[(n - 1) as usize] == '\n' as c_char { n - 1 } else { n };
20618        BUF[end as usize] = 0;
20619    } else {
20620        BUF[0] = 0;
20621    }
20622    BUF.as_mut_ptr()
20623}
20624
20625#[no_mangle]
20626pub unsafe extern "C" fn getsubopt(optionp: *mut *mut core::ffi::c_char, tokens: *const *const core::ffi::c_char, valuep: *mut *mut core::ffi::c_char) -> i32 {
20627    let s = *optionp;
20628    if s.is_null() { return -1; }
20629    // Find end of suboption
20630    let mut end = s;
20631    while *end != 0 && *end != ',' as c_char && *end != '=' as c_char { end = end.add(1); }
20632    let eq = *end == '=' as c_char;
20633    if eq { *end = 0; *valuep = end.add(1); }
20634    // Find matching token
20635    let mut i = 0;
20636    loop {
20637        let tok = *tokens.add(i);
20638        if tok.is_null() { break; }
20639        if crate::strcmp(s as *const u8, tok as *const u8) == 0 {
20640            // Skip to next option
20641    while *end != 0 && *end != ',' as c_char { end = end.add(1); }
20642            if *end == ',' as c_char { end = end.add(1); }
20643            *optionp = end;
20644            return i as i32;
20645        }
20646        i += 1;
20647    }
20648    -1
20649}
20650
20651#[no_mangle]
20652pub unsafe extern "C" fn getw(f: *mut core::ffi::c_void) -> i32 {
20653    let c0 = getc(f as *mut FILE);
20654    if c0 < 0 { return -1; }
20655    let c1 = getc(f as *mut FILE);
20656    if c1 < 0 { return -1; }
20657    let c2 = getc(f as *mut FILE);
20658    if c2 < 0 { return -1; }
20659    let c3 = getc(f as *mut FILE);
20660    if c3 < 0 { return -1; }
20661    (c0 | (c1 << 8) | (c2 << 16) | (c3 << 24)) as i32
20662}
20663
20664#[no_mangle]
20665pub unsafe extern "C" fn getwc(f: *mut core::ffi::c_void) -> u32 {
20666    getc(f as *mut FILE) as u32
20667}
20668
20669#[no_mangle]
20670pub unsafe extern "C" fn init_module(module_image: *mut u8, len: usize, param_values: *const core::ffi::c_char) -> i32 {
20671    syscall_ret(<Arch as Syscalls>::syscall3(SYS_INIT_MODULE, module_image as i64, len as i64, param_values as i64)) as i32
20672}
20673
20674
20675
20676#[no_mangle]
20677pub unsafe extern "C" fn issetugid() -> i32 {
20678    let ruid = crate::getuid();
20679    let euid = crate::geteuid();
20680    let rgid = crate::getgid();
20681    let egid = crate::getegid();
20682    (ruid != euid || rgid != egid) as i32
20683}
20684
20685// ============================================================
20686// poll functions
20687// ============================================================
20688
20689#[repr(C)]
20690struct PollTimespec {
20691    tv_sec: i64,
20692    tv_nsec: i64,
20693}
20694
20695#[no_mangle]
20696pub unsafe extern "C" fn poll(fds: *mut core::ffi::c_void, nfds: u32, timeout: i32) -> i32 {
20697    #[cfg(target_arch = "x86_64")]
20698    { syscall_ret(<Arch as Syscalls>::syscall3(SYS_POLL, fds as i64, nfds as i64, timeout as i64)) as i32 }
20699    #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
20700    {
20701        // poll(2) does not exist on these architectures.  ppoll(2) takes a
20702        // struct timespec, not a millisecond count; passing the integer
20703        // directly made poll(..., 0) wait forever.
20704        let mut ts = PollTimespec { tv_sec: 0, tv_nsec: 0 };
20705        let ts_ptr = if timeout < 0 {
20706            core::ptr::null()
20707        } else {
20708            ts.tv_sec = (timeout / 1000) as i64;
20709            ts.tv_nsec = ((timeout % 1000) as i64) * 1_000_000;
20710            &ts as *const PollTimespec as *const core::ffi::c_void
20711        };
20712        syscall_ret(<Arch as Syscalls>::syscall5(
20713            SYS_PPOLL,
20714            fds as i64,
20715            nfds as i64,
20716            ts_ptr as i64,
20717            0,
20718            8,
20719        )) as i32
20720    }
20721}
20722
20723#[no_mangle]
20724pub unsafe extern "C" fn ppoll(fds: *mut core::ffi::c_void, nfds: u32, timeout: *const core::ffi::c_void, sigmask: *const u64) -> i32 {
20725    syscall_ret(<Arch as Syscalls>::syscall5(SYS_PPOLL, fds as i64, nfds as i64, timeout as i64, sigmask as i64, 8)) as i32
20726}
20727
20728#[no_mangle]
20729pub unsafe extern "C" fn pselect(nfds: i32, readfds: *mut u8, writefds: *mut u8, exceptfds: *mut u8, timeout: *const core::ffi::c_void, sigmask: *const u64) -> i32 {
20730    syscall_ret(<Arch as Syscalls>::syscall6(SYS_PSELECT6, nfds as i64, readfds as i64, writefds as i64, exceptfds as i64, timeout as i64, sigmask as i64)) as i32
20731}
20732
20733// ============================================================
20734// realpath function
20735// ============================================================
20736
20737// ============================================================
20738// quick_exit function
20739// ============================================================
20740
20741#[no_mangle]
20742pub unsafe extern "C" fn quick_exit(status: i32) -> ! {
20743    crate::_exit(status)
20744}
20745
20746// ============================================================
20747// stat functions
20748// ============================================================
20749
20750#[no_mangle]
20751pub unsafe extern "C" fn lstat(path: *const core::ffi::c_char, buf: *mut core::ffi::c_void) -> i32 {
20752    let r = <Arch as Syscalls>::syscall4(SYS_NEWFSTATAT, AT_FDCWD as i64, path as i64, buf as i64, 0x100);
20753    if r < 0 {
20754        ERRNO!() = (-r) as c_int;
20755        -1
20756    } else {
20757        0
20758    }
20759}
20760
20761#[no_mangle]
20762pub unsafe extern "C" fn __xstat(_ver: i32, path: *const core::ffi::c_char, buf: *mut core::ffi::c_void) -> i32 {
20763    crate::stat(path, buf as *mut Stat)
20764}
20765
20766#[no_mangle]
20767pub unsafe extern "C" fn __lxstat(_ver: i32, path: *const core::ffi::c_char, buf: *mut core::ffi::c_void) -> i32 {
20768    crate::lstat(path, buf)
20769}
20770
20771#[no_mangle]
20772pub unsafe extern "C" fn __fxstat(_ver: i32, fd: i32, buf: *mut core::ffi::c_void) -> i32 {
20773    crate::fstat(fd, buf as *mut Stat)
20774}
20775
20776#[no_mangle]
20777pub unsafe extern "C" fn __fxstatat(_ver: i32, _dirfd: i32, path: *const core::ffi::c_char, buf: *mut core::ffi::c_void, _flags: i32) -> i32 {
20778    crate::stat(path, buf as *mut Stat)
20779}
20780
20781// ============================================================
20782// mempcpy function
20783// ============================================================
20784
20785#[no_mangle]
20786pub unsafe extern "C" fn mempcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 {
20787    core::ptr::copy_nonoverlapping(src, dest, n);
20788    dest.add(n)
20789}
20790
20791// ============================================================
20792// mktemp functions
20793// ============================================================
20794
20795#[no_mangle]
20796pub unsafe extern "C" fn mkostemp(template: *mut core::ffi::c_char, _flags: i32) -> i32 {
20797    mkstemp(template)
20798}
20799
20800#[no_mangle]
20801pub unsafe extern "C" fn mkstemps(template: *mut core::ffi::c_char, suffixlen: i32) -> i32 {
20802    mkstemps_impl(template, suffixlen)
20803}
20804
20805// ============================================================
20806// system functions
20807// ============================================================
20808
20809#[no_mangle]
20810pub unsafe extern "C" fn ptrace(request: i32, mut args: ...) -> i64 {
20811    let pid = args.next_arg::<c_long>();
20812    let addr = args.next_arg::<c_long>();
20813    let data = args.next_arg::<c_long>();
20814    // PEEKTEXT/PEEKDATA/PEEKUSER write the result through the data pointer
20815    // (the kernel returns EFAULT for a NULL pointer), so supply a local slot.
20816    if (1..=3).contains(&request) {
20817        let mut result: c_long = 0;
20818        let r = <Arch as Syscalls>::syscall4(
20819            sysno(101, 117),
20820            request as i64,
20821            pid,
20822            addr,
20823            &mut result as *mut c_long as i64,
20824        );
20825        if r < 0 {
20826            ERRNO!() = (-r) as c_int;
20827            return -1;
20828        }
20829        return result;
20830    }
20831    syscall_ret(<Arch as Syscalls>::syscall4(sysno(101, 117), request as i64, pid, addr, data))
20832}
20833
20834#[no_mangle]
20835pub unsafe extern "C" fn personality(persona: u64) -> i64 {
20836    <Arch as Syscalls>::syscall1(sysno(135, 92), persona as i64)
20837}
20838
20839#[no_mangle]
20840pub unsafe extern "C" fn reboot(cmd: c_int) -> i32 {
20841    // musl's public API is reboot(cmd), not the four-argument syscall.
20842    syscall_ret(<Arch as Syscalls>::syscall3(
20843        SYS_REBOOT,
20844        0xfee1dead,
20845        672274793,
20846        cmd as i64,
20847    )) as i32
20848}
20849
20850#[no_mangle]
20851pub unsafe extern "C" fn pivot_root(new_root: *const core::ffi::c_char, put_old: *const core::ffi::c_char) -> i32 {
20852    syscall_ret(<Arch as Syscalls>::syscall2(SYS_PIVOT_ROOT, new_root as i64, put_old as i64)) as i32
20853}
20854
20855#[no_mangle]
20856pub unsafe extern "C" fn mount(source: *const core::ffi::c_char, target: *const core::ffi::c_char, filesystemtype: *const core::ffi::c_char, mountflags: u64, data: *const core::ffi::c_void) -> i32 {
20857    syscall_ret(<Arch as Syscalls>::syscall5(SYS_MOUNT, source as i64, target as i64, filesystemtype as i64, mountflags as i64, data as i64)) as i32
20858}
20859
20860#[no_mangle]
20861pub unsafe extern "C" fn umount(target: *const core::ffi::c_char) -> i32 {
20862    syscall_ret(<Arch as Syscalls>::syscall2(SYS_UMOUNT2, target as i64, 0)) as i32
20863}
20864
20865#[no_mangle]
20866pub unsafe extern "C" fn umount2(target: *const core::ffi::c_char, flags: i32) -> i32 {
20867    syscall_ret(<Arch as Syscalls>::syscall2(SYS_UMOUNT2, target as i64, flags as i64)) as i32
20868}
20869
20870// ============================================================
20871// resolver functions
20872// ============================================================
20873
20874
20875// ============================================================
20876// signal/error functions
20877// ============================================================
20878
20879#[no_mangle]
20880pub unsafe extern "C" fn psiginfo(_pinfo: *const core::ffi::c_void, s: *const core::ffi::c_char) {
20881    if !s.is_null() { crate::write(2, s as *const core::ffi::c_void, crate::strlen(s)); }
20882    crate::write(2, b"\n".as_ptr() as *const core::ffi::c_void, 1);
20883}
20884
20885#[no_mangle]
20886pub unsafe extern "C" fn psignal(sig: i32, s: *const core::ffi::c_char) {
20887    if !s.is_null() { crate::write(2, s as *const core::ffi::c_void, crate::strlen(s)); }
20888    crate::write(2, b": ".as_ptr() as *const core::ffi::c_void, 2);
20889    crate::write(2, strsignal(sig) as *const core::ffi::c_void, crate::strlen(strsignal(sig)));
20890    crate::write(2, b"\n".as_ptr() as *const core::ffi::c_void, 1);
20891}
20892
20893#[no_mangle]
20894pub unsafe extern "C" fn strsignal(sig: c_int) -> *const c_char {
20895    static NAMES: [&[u8]; 32] = [
20896        b"Unknown signal\0",
20897        b"Hangup\0",
20898        b"Interrupt\0",
20899        b"Quit\0",
20900        b"Illegal instruction\0",
20901        b"Trace/breakpoint trap\0",
20902        b"Aborted\0",
20903        b"Bus error\0",
20904        b"Floating point exception\0",
20905        b"Killed\0",
20906        b"User defined signal 1\0",
20907        b"Segmentation fault\0",
20908        b"User defined signal 2\0",
20909        b"Broken pipe\0",
20910        b"Alarm clock\0",
20911        b"Terminated\0",
20912        b"Stack fault\0",
20913        b"Child process status\0",
20914        b"Continued\0",
20915        b"Stopped (signal)\0",
20916        b"Stopped\0",
20917        b"Stopped (tty input)\0",
20918        b"Stopped (tty output)\0",
20919        b"Urgent I/O condition\0",
20920        b"CPU time limit exceeded\0",
20921        b"File size limit exceeded\0",
20922        b"Virtual timer expired\0",
20923        b"Profiling timer expired\0",
20924        b"Window changed\0",
20925        b"I/O possible\0",
20926        b"Power failure\0",
20927        b"Bad system call\0",
20928    ];
20929    static mut RT_BUF: [c_char; 32] = [0; 32];
20930    if sig > 0 && sig < 32 {
20931        return NAMES[sig as usize].as_ptr() as *const c_char;
20932    }
20933    if sig >= 32 && sig < 65 {
20934        let mut o = 0usize;
20935        for &c in b"Real-time signal \0" {
20936            if c == 0 { break; }
20937            RT_BUF[o] = c as c_char;
20938            o += 1;
20939        }
20940        let mut v = (sig - 32) as u32;
20941        let mut digits = [0u8; 10];
20942        let mut d = 0usize;
20943        if v == 0 { digits[0] = b'0'; d = 1; }
20944        while v > 0 { digits[d] = b'0' + (v % 10) as u8; v /= 10; d += 1; }
20945        while d > 0 { d -= 1; RT_BUF[o] = digits[d] as c_char; o += 1; }
20946        RT_BUF[o] = 0;
20947        return RT_BUF.as_ptr();
20948    }
20949    b"Unknown signal\0".as_ptr() as *const c_char
20950}
20951
20952#[no_mangle]
20953pub unsafe extern "C" fn __strerror_l(errnum: i32, _loc: *mut core::ffi::c_void) -> *const core::ffi::c_char {
20954    crate::strerror(errnum)
20955}
20956
20957// ============================================================
20958// preadv functions
20959// ============================================================
20960
20961#[no_mangle]
20962pub unsafe extern "C" fn preadv(fd: i32, iov: *const core::ffi::c_void, iovcnt: i32, offset: i64) -> isize {
20963    // preadv takes a 64-bit offset split into pos_l/pos_h on x86_64 and the
20964    // generic ABI; passing only pos_l leaves the high half undefined.
20965    syscall_ret(<Arch as Syscalls>::syscall5(
20966        sysno(295, 69),
20967        fd as i64,
20968        iov as i64,
20969        iovcnt as i64,
20970        offset,
20971        offset >> 32,
20972    )) as isize
20973}
20974
20975#[no_mangle]
20976pub unsafe extern "C" fn pwritev(fd: i32, iov: *const core::ffi::c_void, iovcnt: i32, offset: i64) -> isize {
20977    syscall_ret(<Arch as Syscalls>::syscall5(
20978        sysno(296, 70),
20979        fd as i64,
20980        iov as i64,
20981        iovcnt as i64,
20982        offset,
20983        offset >> 32,
20984    )) as isize
20985}
20986
20987#[no_mangle]
20988pub unsafe extern "C" fn preadv2(fd: i32, iov: *const core::ffi::c_void, iovcnt: i32, offset: i64, flags: u32) -> isize {
20989    // x86_64/generic preadv2 split the 64-bit offset into pos_l/pos_h and
20990    // take the flags as the sixth argument.
20991    syscall_ret(<Arch as Syscalls>::syscall6(
20992        sysno(327, 286),
20993        fd as i64,
20994        iov as i64,
20995        iovcnt as i64,
20996        offset,
20997        offset >> 32,
20998        flags as i64,
20999    )) as isize
21000}
21001
21002#[no_mangle]
21003pub unsafe extern "C" fn pwritev2(fd: i32, iov: *const core::ffi::c_void, iovcnt: i32, offset: i64, flags: u32) -> isize {
21004    syscall_ret(<Arch as Syscalls>::syscall6(
21005        sysno(328, 287),
21006        fd as i64,
21007        iov as i64,
21008        iovcnt as i64,
21009        offset,
21010        offset >> 32,
21011        flags as i64,
21012    )) as isize
21013}
21014
21015// ============================================================
21016// nice function
21017// ============================================================
21018
21019#[no_mangle]
21020pub unsafe extern "C" fn nice(inc: i32) -> i32 {
21021    let mut prio = inc;
21022    if inc > -40 && inc < 40 {
21023        prio += getpriority(0, 0);
21024    }
21025    if prio > 19 { prio = 19; }
21026    if prio < -20 { prio = -20; }
21027    if setpriority(0, 0, prio) != 0 {
21028        if ERRNO!() == 13 { ERRNO!() = 1; } // EACCES -> EPERM, as musl does
21029        return -1;
21030    }
21031    prio
21032}
21033
21034// ============================================================
21035// misc functions
21036// ============================================================
21037
21038#[no_mangle]
21039pub unsafe extern "C" fn l64a(value: i64) -> *mut core::ffi::c_char {
21040    static mut BUF: [core::ffi::c_char; 7] = [0; 7];
21041    let mut v = value as u64;
21042    let mut i = 0;
21043    while v > 0 && i < 6 {
21044        let c = (v & 0x3f) as u8;
21045        BUF[i] = if c < 26 { c + b'A' } else if c < 52 { c - 26 + b'a' } else if c < 62 { c - 52 + b'0' } else if c == 62 { b'.' } else { b'/' } as core::ffi::c_char;
21046        v >>= 6;
21047        i += 1;
21048    }
21049    BUF[i] = 0;
21050    BUF.as_mut_ptr()
21051}
21052
21053#[no_mangle]
21054pub unsafe extern "C" fn lchmod(path: *const core::ffi::c_char, mode: u32) -> i32 {
21055    syscall_ret(<Arch as Syscalls>::syscall4(SYS_FCHMODAT, AT_FDCWD as i64, path as i64, mode as i64, 0x100)) as i32
21056}
21057
21058#[no_mangle]
21059pub unsafe extern "C" fn lutimes(filename: *const core::ffi::c_char, times: *const core::ffi::c_void) -> i32 {
21060    utimensat(AT_FDCWD, filename, times as *const timespec, 0x100)
21061}
21062
21063#[no_mangle]
21064pub unsafe extern "C" fn mknod(pathname: *const core::ffi::c_char, mode: u32, dev: u64) -> i32 {
21065    #[cfg(target_arch = "x86_64")]
21066    { syscall_ret(<Arch as Syscalls>::syscall3(SYS_MKNOD, pathname as i64, mode as i64, dev as i64)) as i32 }
21067    #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
21068    {
21069        syscall_ret(<Arch as Syscalls>::syscall4(SYS_MKNODAT, AT_FDCWD as i64, pathname as i64, mode as i64, dev as i64)) as i32
21070    }
21071}
21072
21073#[no_mangle]
21074pub unsafe extern "C" fn memfd_create(name: *const core::ffi::c_char, flags: u32) -> i32 {
21075    syscall_ret(<Arch as Syscalls>::syscall2(sysno(319, 279), name as i64, flags as i64)) as i32
21076}
21077
21078#[no_mangle]
21079pub unsafe extern "C" fn mlock2(addr: *const core::ffi::c_void, len: usize, flags: u32) -> i32 {
21080    syscall_ret(<Arch as Syscalls>::syscall3(sysno(325, 284), addr as i64, len as i64, flags as i64)) as i32
21081}
21082
21083
21084
21085#[no_mangle]
21086pub unsafe extern "C" fn remap_file_pages(addr: *mut core::ffi::c_void, size: usize, prot: i32, pgoff: usize, flags: u32) -> i32 {
21087    syscall_ret(<Arch as Syscalls>::syscall5(sysno(216, 234), addr as i64, size as i64, prot as i64, pgoff as i64, flags as i64)) as i32
21088}
21089
21090// ============================================================
21091// nan functions
21092// ============================================================
21093
21094#[no_mangle]
21095pub unsafe extern "C" fn nan(_tagp: *const core::ffi::c_char) -> f64 {
21096    f64::NAN
21097}
21098
21099#[no_mangle]
21100pub unsafe extern "C" fn nanf(_tagp: *const core::ffi::c_char) -> f32 {
21101    f32::NAN
21102}
21103
21104// ============================================================
21105// Remaining symbols - batch 1
21106// ============================================================
21107
21108#[no_mangle]
21109pub unsafe extern "C" fn __lgammal_r(x: f64, signgamp: *mut i32) -> f64 {
21110    *signgamp = 1;
21111    lgamma(x)
21112}
21113
21114#[no_mangle]
21115pub unsafe extern "C" fn __libc_current_sigrtmax() -> i32 {
21116    64
21117}
21118
21119#[no_mangle]
21120pub unsafe extern "C" fn _longjmp(env: *mut core::ffi::c_void, val: i32) {
21121    crate::longjmp(env as *mut u64, val)
21122}
21123
21124#[no_mangle]
21125pub unsafe extern "C" fn __newlocale(_mask: i32, _name: *const core::ffi::c_char, base: *mut core::ffi::c_void) -> *mut core::ffi::c_void {
21126    base
21127}
21128
21129#[no_mangle]
21130pub unsafe extern "C" fn __nl_langinfo(_item: i32) -> *mut core::ffi::c_char {
21131    b"\0".as_ptr() as *mut core::ffi::c_char
21132}
21133
21134#[no_mangle]
21135pub unsafe extern "C" fn __nl_langinfo_l(_item: i32, _loc: *mut core::ffi::c_void) -> *mut core::ffi::c_char {
21136    b"\0".as_ptr() as *mut core::ffi::c_char
21137}
21138
21139#[no_mangle]
21140pub unsafe extern "C" fn ns_get16(cp: *const u8) -> u16 {
21141    ((*cp.add(0) as u16) << 8) | *cp.add(1) as u16
21142}
21143
21144#[no_mangle]
21145pub unsafe extern "C" fn ns_get32(cp: *const u8) -> u32 {
21146    ((*cp.add(0) as u32) << 24) | ((*cp.add(1) as u32) << 16) | ((*cp.add(2) as u32) << 8) | *cp.add(3) as u32
21147}
21148
21149#[no_mangle]
21150pub unsafe extern "C" fn ns_put16(s: u16, dst: *mut u8) {
21151    *dst.add(0) = (s >> 8) as u8;
21152    *dst.add(1) = (s & 0xff) as u8;
21153}
21154
21155#[no_mangle]
21156pub unsafe extern "C" fn ns_put32(l: u32, dst: *mut u8) {
21157    *dst.add(0) = (l >> 24) as u8;
21158    *dst.add(1) = ((l >> 16) & 0xff) as u8;
21159    *dst.add(2) = ((l >> 8) & 0xff) as u8;
21160    *dst.add(3) = (l & 0xff) as u8;
21161}
21162
21163#[no_mangle]
21164pub unsafe extern "C" fn posix_close(fd: i32) -> i32 {
21165    crate::close(fd)
21166}
21167
21168#[no_mangle]
21169pub unsafe extern "C" fn posix_openpt(flags: i32) -> i32 {
21170    crate::open(b"/dev/ptmx\0".as_ptr() as *const core::ffi::c_char, flags, 0)
21171}
21172
21173#[no_mangle]
21174pub unsafe extern "C" fn putw(w: i32, f: *mut core::ffi::c_void) {
21175    putc((w & 0xff) as i32, f as *mut FILE);
21176    putc(((w >> 8) & 0xff) as i32, f as *mut FILE);
21177    putc(((w >> 16) & 0xff) as i32, f as *mut FILE);
21178    putc(((w >> 24) & 0xff) as i32, f as *mut FILE);
21179}
21180
21181
21182
21183#[no_mangle]
21184pub unsafe extern "C" fn rand_r(seedp: *mut u32) -> i32 {
21185    let seed = (*seedp).wrapping_mul(1103515245).wrapping_add(12345);
21186    *seedp = seed;
21187    ((seed >> 16) & 0x7fff) as i32
21188}
21189
21190#[no_mangle]
21191pub unsafe extern "C" fn reallocarray(ptr: *mut core::ffi::c_void, nmemb: usize, size: usize) -> *mut core::ffi::c_void {
21192    let total = match nmemb.checked_mul(size) {
21193        Some(v) => v,
21194        None => { ERRNO!() = ENOMEM; return core::ptr::null_mut(); }
21195    };
21196    crate::realloc(ptr, total)
21197}
21198
21199#[no_mangle]
21200pub unsafe extern "C" fn __res_state() -> *mut core::ffi::c_void {
21201    static mut STATE: [u8; 512] = [0; 512];
21202    STATE.as_mut_ptr() as *mut core::ffi::c_void
21203}
21204
21205#[no_mangle]
21206pub unsafe extern "C" fn sbrk(increment: isize) -> *mut u8 {
21207    // musl does not support growing the break directly (malloc uses mmap);
21208    // sbrk(nonzero) always fails with ENOMEM.
21209    if increment != 0 {
21210        ERRNO!() = ENOMEM;
21211        return -1isize as usize as *mut u8;
21212    }
21213    <Arch as Syscalls>::syscall1(sysno(12, 214), 0) as *mut u8
21214}
21215
21216#[no_mangle]
21217pub unsafe extern "C" fn scalb(x: f64, exp: f64) -> f64 {
21218    if x.is_nan() || exp.is_nan() { return x * exp; }
21219    if !exp.is_finite() {
21220        let r = if exp > 0.0 { x * exp } else { x / (-exp) };
21221        if r.is_nan() && !x.is_nan() {
21222            feraiseexcept(FE_INVALID);
21223        }
21224        return r;
21225    }
21226    if libm::rint(exp) != exp {
21227        return __math_invalid(exp);
21228    }
21229    if exp > 65000.0 { return libm::scalbn(x, 65000); }
21230    if -exp > 65000.0 { return libm::scalbn(x, -65000); }
21231    libm::scalbn(x, exp as i32)
21232}
21233
21234#[no_mangle]
21235pub unsafe extern "C" fn scalbf(x: f32, exp: f32) -> f32 {
21236    if x.is_nan() || exp.is_nan() { return x * exp; }
21237    if !exp.is_finite() {
21238        let r = if exp > 0.0 { x * exp } else { x / (-exp) };
21239        if r.is_nan() && !x.is_nan() {
21240            feraiseexcept(FE_INVALID);
21241        }
21242        return r;
21243    }
21244    if libm::rintf(exp) != exp {
21245        return __math_invalidf(exp);
21246    }
21247    if exp > 65000.0 { return libm::scalbnf(x, 65000); }
21248    if -exp > 65000.0 { return libm::scalbnf(x, -65000); }
21249    libm::scalbnf(x, exp as i32)
21250}
21251
21252#[no_mangle]
21253pub unsafe extern "C" fn __sched_cpucount(setsize: usize, setp: *const u8) -> i32 {
21254    let mut count = 0;
21255    let mut i = 0;
21256    while i < setsize * 8 {
21257        if (*setp.add(i / 8) >> (i % 8)) & 1 != 0 { count += 1; }
21258        i += 1;
21259    }
21260    count
21261}
21262
21263
21264
21265#[no_mangle]
21266pub unsafe extern "C" fn select(nfds: i32, readfds: *mut u8, writefds: *mut u8, exceptfds: *mut u8, timeout: *mut core::ffi::c_void) -> i32 {
21267    syscall_ret(<Arch as Syscalls>::syscall6(sysno(23, 72), nfds as i64, readfds as i64, writefds as i64, exceptfds as i64, timeout as i64, 0)) as i32
21268}
21269
21270
21271
21272#[no_mangle]
21273pub unsafe extern "C" fn sendfile(out_fd: i32, in_fd: i32, offset: *mut i64, count: usize) -> isize {
21274    syscall_ret(<Arch as Syscalls>::syscall4(sysno(40, 71), out_fd as i64, in_fd as i64, offset as i64, count as i64)) as isize
21275}
21276
21277
21278
21279
21280
21281#[no_mangle]
21282pub unsafe extern "C" fn __setjmp(env: *mut core::ffi::c_void) -> i32 {
21283    crate::setjmp(env as *mut u64)
21284}
21285
21286#[no_mangle]
21287pub unsafe extern "C" fn _setjmp(env: *mut core::ffi::c_void) -> i32 {
21288    crate::setjmp(env as *mut u64)
21289}
21290
21291#[no_mangle]
21292pub unsafe extern "C" fn setlinebuf(stream: *mut core::ffi::c_void) {
21293    setvbuf(stream as *mut FILE, core::ptr::null_mut(), 1, 0);
21294}
21295
21296
21297
21298
21299
21300
21301
21302
21303
21304
21305
21306
21307
21308
21309
21310#[no_mangle]
21311pub unsafe extern "C" fn significand(x: f64) -> f64 {
21312    let bits = x.to_bits();
21313    let exp = ((bits >> 52) & 0x7ff) as i32;
21314    if exp == 0 || exp == 0x7ff { return x; }
21315    f64::from_bits((bits & 0x000fffffffffffff) | (1023u64 << 52))
21316}
21317
21318#[no_mangle]
21319pub unsafe extern "C" fn significandf(x: f32) -> f32 {
21320    let bits = x.to_bits();
21321    let exp = ((bits >> 23) & 0xff) as i32;
21322    if exp == 0 || exp == 0xff { return x; }
21323    f32::from_bits((bits & 0x007fffff) | (127u32 << 23))
21324}
21325
21326
21327
21328
21329
21330#[no_mangle]
21331pub unsafe extern "C" fn sigset(sig: i32, handler: usize) -> usize {
21332    // BSD/glibc-compatible sigset() is an alias for signal().
21333    signal(sig, handler)
21334}
21335
21336#[no_mangle]
21337pub unsafe extern "C" fn sincos(x: f64, sin: *mut f64, cos: *mut f64) {
21338    *sin = libm::sin(x);
21339    *cos = libm::cos(x);
21340}
21341
21342#[no_mangle]
21343pub unsafe extern "C" fn sincosf(x: f32, sin: *mut f32, cos: *mut f32) {
21344    *sin = libm::sinf(x);
21345    *cos = libm::cosf(x);
21346}
21347
21348
21349
21350
21351
21352
21353
21354
21355
21356unsafe fn nd_ascii_lower(c: c_char) -> c_int {
21357    let b = c as u8;
21358    if b >= b'A' && b <= b'Z' { (b + 32) as c_int } else { b as c_int }
21359}
21360
21361unsafe fn nd_strncasecmp(s1: *const c_char, s2: *const c_char, n: usize) -> c_int {
21362    let mut i = 0usize;
21363    while i < n {
21364        let c1 = nd_ascii_lower(*s1.add(i));
21365        let c2 = nd_ascii_lower(*s2.add(i));
21366        if c1 != c2 || c1 == 0 { return c1 - c2; }
21367        i += 1;
21368    }
21369    0
21370}
21371
21372#[no_mangle]
21373pub unsafe extern "C" fn strcasecmp(s1: *const core::ffi::c_char, s2: *const core::ffi::c_char) -> i32 {
21374    nd_strncasecmp(s1, s2, usize::MAX)
21375}
21376
21377#[no_mangle]
21378pub unsafe extern "C" fn __strcasecmp_l(s1: *const core::ffi::c_char, s2: *const core::ffi::c_char, _loc: *mut core::ffi::c_void) -> i32 {
21379    nd_strncasecmp(s1, s2, usize::MAX)
21380}
21381
21382#[no_mangle]
21383pub unsafe extern "C" fn strcasestr(haystack: *const core::ffi::c_char, needle: *const core::ffi::c_char) -> *mut core::ffi::c_char {
21384    let nlen = crate::strlen(needle);
21385    if nlen == 0 { return haystack as *mut core::ffi::c_char; }
21386    let mut i = 0;
21387    loop {
21388        let h = haystack.add(i);
21389        if *h == 0 { return core::ptr::null_mut(); }
21390        if crate::strncasecmp(h, needle, nlen) == 0 { return h as *mut core::ffi::c_char; }
21391        i += 1;
21392    }
21393}
21394
21395#[no_mangle]
21396pub unsafe extern "C" fn __strcoll_l(s1: *const core::ffi::c_char, s2: *const core::ffi::c_char, _loc: *mut core::ffi::c_void) -> i32 {
21397    crate::strcmp(s1 as *const u8, s2 as *const u8)
21398}
21399
21400#[no_mangle]
21401pub unsafe extern "C" fn strncasecmp(s1: *const core::ffi::c_char, s2: *const core::ffi::c_char, n: usize) -> i32 {
21402    nd_strncasecmp(s1, s2, n)
21403}
21404
21405#[no_mangle]
21406pub unsafe extern "C" fn __strncasecmp_l(s1: *const core::ffi::c_char, s2: *const core::ffi::c_char, n: usize, _loc: *mut core::ffi::c_void) -> i32 {
21407    nd_strncasecmp(s1, s2, n)
21408}
21409
21410#[no_mangle]
21411pub unsafe extern "C" fn strsep(stringp: *mut *mut core::ffi::c_char, delim: *const core::ffi::c_char) -> *mut core::ffi::c_char {
21412    let s = *stringp;
21413    if s.is_null() { return core::ptr::null_mut(); }
21414    let mut p = s;
21415    while *p != 0 {
21416        let mut d = delim;
21417        while *d != 0 {
21418            if *p == *d { *p = 0; *stringp = p.add(1); return s; }
21419            d = d.add(1);
21420        }
21421        p = p.add(1);
21422    }
21423    *stringp = core::ptr::null_mut();
21424    s
21425}
21426
21427#[no_mangle]
21428pub unsafe extern "C" fn strtoimax(nptr: *const core::ffi::c_char, endptr: *mut *mut core::ffi::c_char, base: i32) -> i64 {
21429    crate::strtol(nptr, endptr, base)
21430}
21431
21432#[no_mangle]
21433pub unsafe extern "C" fn strtok_r(s: *mut core::ffi::c_char, delim: *const core::ffi::c_char, saveptr: *mut *mut core::ffi::c_char) -> *mut core::ffi::c_char {
21434    let mut s = if s.is_null() { *saveptr } else { s };
21435    if s.is_null() { return core::ptr::null_mut(); }
21436    // Skip leading delimiters
21437    while *s != 0 {
21438        let mut d = delim;
21439        let mut found = false;
21440        while *d != 0 {
21441            if *s == *d { found = true; break; }
21442            d = d.add(1);
21443        }
21444        if !found { break; }
21445        s = s.add(1);
21446    }
21447    if *s == 0 { *saveptr = s; return core::ptr::null_mut(); }
21448    let start = s;
21449    // Find end of token
21450    while *s != 0 {
21451        let mut d = delim;
21452        while *d != 0 {
21453            if *s == *d { *s = 0; *saveptr = s.add(1); return start; }
21454            d = d.add(1);
21455        }
21456        s = s.add(1);
21457    }
21458    *saveptr = s;
21459    start
21460}
21461
21462#[no_mangle]
21463pub unsafe extern "C" fn strtoumax(nptr: *const core::ffi::c_char, endptr: *mut *mut core::ffi::c_char, base: i32) -> u64 {
21464    crate::strtoul(nptr, endptr, base) as u64
21465}
21466
21467#[no_mangle]
21468pub unsafe extern "C" fn __strxfrm_l(dest: *mut core::ffi::c_char, src: *const core::ffi::c_char, n: usize, _loc: *mut core::ffi::c_void) -> usize {
21469    crate::strlcpy(dest as *mut u8, src as *const u8, n)
21470}
21471
21472#[no_mangle]
21473pub unsafe extern "C" fn swab(src: *const u8, dest: *mut u8, nbytes: isize) {
21474    let mut i = 0;
21475    while i + 1 < nbytes {
21476        let a = *src.add(i as usize);
21477        let b = *src.add(i as usize + 1);
21478        *dest.add(i as usize) = b;
21479        *dest.add(i as usize + 1) = a;
21480        i += 2;
21481    }
21482}
21483
21484
21485
21486
21487
21488
21489
21490
21491
21492
21493
21494#[no_mangle]
21495pub unsafe extern "C" fn __tolower_l(c: i32, _loc: *mut core::ffi::c_void) -> i32 {
21496    if c >= 'A' as i32 && c <= 'Z' as i32 { c + 32 } else { c }
21497}
21498
21499#[no_mangle]
21500pub unsafe extern "C" fn __toupper_l(c: i32, _loc: *mut core::ffi::c_void) -> i32 {
21501    if c >= 'a' as i32 && c <= 'z' as i32 { c - 32 } else { c }
21502}
21503
21504#[no_mangle]
21505pub unsafe extern "C" fn ulimit(cmd: c_int, mut args: ...) -> c_long {
21506    let mut rl: Rlimit = core::mem::zeroed();
21507    if getrlimit(RLIMIT_FSIZE, &mut rl) != 0 { return -1; }
21508    if cmd == 2 {
21509        let val = args.next_arg::<c_long>();
21510        rl.rlim_cur = 512u64.wrapping_mul(val as u64);
21511        if setrlimit(RLIMIT_FSIZE, &rl) != 0 { return -1; }
21512    }
21513    (rl.rlim_cur / 512) as c_long
21514}
21515
21516#[repr(C)]
21517struct UtsName {
21518    sysname: [u8; 65],
21519    nodename: [u8; 65],
21520    release: [u8; 65],
21521    version: [u8; 65],
21522    machine: [u8; 65],
21523    domainname: [u8; 65],
21524}
21525
21526#[no_mangle]
21527pub unsafe extern "C" fn uname(buf: *mut c_void) -> c_int {
21528    // Let the kernel validate the user pointer: an invalid but non-null
21529    // address must produce EFAULT instead of a fault in a local copy.
21530    let r = <Arch as Syscalls>::syscall1(SYS_UNAME, buf as i64);
21531    if r < 0 { ERRNO!() = (-r) as c_int; return -1; }
21532    0
21533}
21534
21535#[no_mangle]
21536pub unsafe extern "C" fn unshare(flags: i32) -> i32 {
21537    let r = <Arch as Syscalls>::syscall1(SYS_UNSHARE, flags as i64);
21538    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
21539}
21540
21541#[no_mangle]
21542pub unsafe extern "C" fn updwtmpx(wtmpx_file: *const core::ffi::c_char, utx: *const core::ffi::c_void) {
21543    updwtmp(wtmpx_file, utx)
21544}
21545
21546#[no_mangle]
21547pub unsafe extern "C" fn __uselocale(loc: *mut core::ffi::c_void) -> *mut core::ffi::c_void {
21548    loc
21549}
21550
21551#[repr(C)]
21552pub struct Utimbuf { actime: i64, modtime: i64 }
21553
21554#[no_mangle]
21555pub unsafe extern "C" fn utime(filename: *const c_char, times: *const Utimbuf) -> c_int {
21556    if times.is_null() {
21557        return utimensat(AT_FDCWD, filename, core::ptr::null(), 0);
21558    }
21559    let ts = [
21560        timespec { tv_sec: (*times).actime, tv_nsec: 0 },
21561        timespec { tv_sec: (*times).modtime, tv_nsec: 0 },
21562    ];
21563    utimensat(AT_FDCWD, filename, ts.as_ptr(), 0)
21564}
21565
21566#[no_mangle]
21567pub unsafe extern "C" fn vhangup() -> c_int {
21568    let r = <Arch as Syscalls>::syscall0(SYS_VHANGUP);
21569    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
21570}
21571
21572#[no_mangle]
21573pub unsafe extern "C" fn vmsplice(fd: i32, iov: *const c_void, nr_segs: usize, flags: u32) -> isize {
21574    let r = <Arch as Syscalls>::syscall4(SYS_VMSPLICE, fd as i64, iov as i64, nr_segs as i64, flags as i64);
21575    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { r as isize }
21576}
21577
21578
21579
21580#[no_mangle]
21581pub unsafe extern "C" fn wait4(pid: i32, wstatus: *mut i32, options: i32, rusage: *mut core::ffi::c_void) -> i32 {
21582    syscall_ret(<Arch as Syscalls>::syscall4(SYS_WAIT4, pid as i64, wstatus as i64, options as i64, rusage as i64)) as i32
21583}
21584
21585#[no_mangle]
21586pub unsafe extern "C" fn waitid(idtype: i32, id: u32, infop: *mut core::ffi::c_void, options: i32) -> i32 {
21587    // The kernel takes a fifth rusage pointer; it must be NULL, not whatever
21588    // happens to be in r10.
21589    syscall_ret(<Arch as Syscalls>::syscall5(
21590        sysno(247, 95),
21591        idtype as i64,
21592        id as i64,
21593        infop as i64,
21594        options as i64,
21595        0,
21596    )) as i32
21597}
21598
21599#[no_mangle]
21600pub unsafe extern "C" fn __wcscoll_l(s1: *const u32, s2: *const u32, _loc: *mut core::ffi::c_void) -> i32 {
21601    crate::wcscmp(s1 as *const i32, s2 as *const i32)
21602}
21603
21604#[no_mangle]
21605pub unsafe extern "C" fn __wcsxfrm_l(dest: *mut u32, src: *const u32, n: usize, _loc: *mut core::ffi::c_void) -> usize {
21606    crate::wcscpy(dest as *mut i32, src as *const i32);
21607    n
21608}
21609
21610#[no_mangle]
21611pub unsafe extern "C" fn wcwidth(c: u32) -> i32 {
21612    if c == 0 { return 0; }
21613    if c < 32 || (c >= 0x7f && c < 0xa0) { return -1; }
21614    1
21615}
21616
21617
21618
21619
21620
21621// ============================================================
21622// Final missing symbols
21623// ============================================================
21624
21625
21626
21627#[no_mangle]
21628pub unsafe extern "C" fn strtod_l(nptr: *const core::ffi::c_char, endptr: *mut *mut core::ffi::c_char, _loc: *mut core::ffi::c_void) -> f64 {
21629    crate::strtod(nptr, endptr)
21630}
21631
21632#[no_mangle]
21633pub unsafe extern "C" fn strtof_l(nptr: *const core::ffi::c_char, endptr: *mut *mut core::ffi::c_char, _loc: *mut core::ffi::c_void) -> f32 {
21634    crate::strtof(nptr, endptr)
21635}
21636
21637#[no_mangle]
21638pub unsafe extern "C" fn strtold_l(nptr: *const core::ffi::c_char, endptr: *mut *mut core::ffi::c_char, _loc: *mut core::ffi::c_void) -> f64 {
21639    crate::strtod(nptr, endptr)
21640}
21641
21642// ============================================================
21643// Directory operations - real implementations
21644// ============================================================
21645
21646#[repr(C)]
21647struct linux_dirent64 {
21648    d_ino: u64,
21649    d_off: i64,
21650    d_reclen: u16,
21651    d_type: u8,
21652    d_name: [u8; 0], // flexible array
21653}
21654
21655// Directory streams used to be a process-wide singleton, which broke any
21656// nested opendir/readdir sequence (git, find, glob, ...). Each DIR now owns
21657// its fd and read buffer, like musl's __dirstream.
21658const DIR_BUF_SIZE: usize = 4096;
21659
21660#[repr(C)]
21661struct DirStream {
21662    fd: i32,
21663    pos: usize,
21664    len: usize,
21665    last_off: i64,
21666    buf: [u8; DIR_BUF_SIZE],
21667}
21668
21669unsafe fn dir_stream_new(fd: i32) -> *mut core::ffi::c_void {
21670    let ds = malloc(core::mem::size_of::<DirStream>()) as *mut DirStream;
21671    if ds.is_null() {
21672        crate::close(fd);
21673        ERRNO!() = ENOMEM;
21674        return core::ptr::null_mut();
21675    }
21676    core::ptr::write_bytes(ds, 0, 1);
21677    (*ds).fd = fd;
21678    ds as *mut core::ffi::c_void
21679}
21680
21681#[no_mangle]
21682pub unsafe extern "C" fn opendir(dirname: *const core::ffi::c_char) -> *mut core::ffi::c_void {
21683    let fd = openat(AT_FDCWD, dirname, O_DIRECTORY, 0); // O_RDONLY | O_DIRECTORY
21684    if fd < 0 { return core::ptr::null_mut(); }
21685    dir_stream_new(fd)
21686}
21687
21688#[no_mangle]
21689pub unsafe extern "C" fn closedir(dirp: *mut core::ffi::c_void) -> i32 {
21690    if dirp.is_null() { ERRNO!() = 22; return -1; }
21691    let ds = dirp as *mut DirStream;
21692    let r = crate::close((*ds).fd);
21693    free(dirp);
21694    r
21695}
21696
21697#[no_mangle]
21698pub unsafe extern "C" fn readdir(dirp: *mut core::ffi::c_void) -> *mut core::ffi::c_void {
21699    if dirp.is_null() { ERRNO!() = 22; return core::ptr::null_mut(); }
21700    let ds = &mut *(dirp as *mut DirStream);
21701
21702    loop {
21703        if ds.pos >= ds.len {
21704            let nread = syscall_ret(<Arch as Syscalls>::syscall3(
21705                SYS_GETDENTS64,
21706                ds.fd as i64,
21707                ds.buf.as_mut_ptr() as i64,
21708                DIR_BUF_SIZE as i64,
21709            )) as isize;
21710            if nread <= 0 { return core::ptr::null_mut(); }
21711            ds.len = nread as usize;
21712            ds.pos = 0;
21713        }
21714
21715        let ent = ds.buf.as_ptr().add(ds.pos) as *const linux_dirent64;
21716        let ent_ref = &*ent;
21717        if ent_ref.d_reclen as usize == 0 {
21718            ds.len = 0;
21719            return core::ptr::null_mut();
21720        }
21721        ds.pos += ent_ref.d_reclen as usize;
21722        ds.last_off = ent_ref.d_off;
21723
21724        // Return a pointer into this stream's buffer (valid until next call).
21725        return ent as *mut core::ffi::c_void;
21726    }
21727}
21728
21729#[no_mangle]
21730pub unsafe extern "C" fn readdir_r(dirp: *mut core::ffi::c_void, _entry: *mut core::ffi::c_void, result: *mut *mut core::ffi::c_void) -> i32 {
21731    let ent = readdir(dirp);
21732    if ent.is_null() {
21733        *result = core::ptr::null_mut();
21734    } else {
21735        *result = ent;
21736    }
21737    0
21738}
21739
21740#[no_mangle]
21741pub unsafe extern "C" fn seekdir(dirp: *mut core::ffi::c_void, loc: i64) {
21742    if dirp.is_null() { return; }
21743    let ds = &mut *(dirp as *mut DirStream);
21744    <Arch as Syscalls>::syscall3(SYS_LSEEK, ds.fd as i64, loc, 0);
21745    ds.pos = 0;
21746    ds.len = 0;
21747}
21748
21749#[no_mangle]
21750pub unsafe extern "C" fn telldir(dirp: *mut core::ffi::c_void) -> i64 {
21751    if dirp.is_null() { return -1; }
21752    (*(dirp as *mut DirStream)).last_off
21753}
21754
21755#[no_mangle]
21756pub unsafe extern "C" fn rewinddir(dirp: *mut core::ffi::c_void) {
21757    if dirp.is_null() { return; }
21758    let ds = &mut *(dirp as *mut DirStream);
21759    <Arch as Syscalls>::syscall3(SYS_LSEEK, ds.fd as i64, 0, 0);
21760    ds.pos = 0;
21761    ds.len = 0;
21762    ds.last_off = 0;
21763}
21764
21765#[no_mangle]
21766pub unsafe extern "C" fn dirfd(dirp: *mut core::ffi::c_void) -> i32 {
21767    if dirp.is_null() { ERRNO!() = 22; return -1; }
21768    (*(dirp as *mut DirStream)).fd
21769}
21770
21771// ============================================================
21772// Memory management - real implementations
21773// ============================================================
21774
21775#[no_mangle]
21776pub unsafe extern "C" fn mprotect(addr: *mut u8, len: usize, prot: i32) -> i32 {
21777    syscall_ret(<Arch as Syscalls>::syscall3(SYS_MPROTECT, addr as i64, len as i64, prot as i64)) as i32
21778}
21779
21780#[no_mangle]
21781pub unsafe extern "C" fn msync(addr: *mut u8, len: usize, flags: i32) -> i32 {
21782    syscall_ret(<Arch as Syscalls>::syscall3(sysno(26, 227), addr as i64, len as i64, flags as i64)) as i32
21783}
21784
21785#[no_mangle]
21786pub unsafe extern "C" fn mlock(addr: *const u8, len: usize) -> i32 {
21787    syscall_ret(<Arch as Syscalls>::syscall2(sysno(149, 228), addr as i64, len as i64)) as i32
21788}
21789
21790#[no_mangle]
21791pub unsafe extern "C" fn munlock(addr: *const u8, len: usize) -> i32 {
21792    syscall_ret(<Arch as Syscalls>::syscall2(sysno(150, 229), addr as i64, len as i64)) as i32
21793}
21794
21795#[no_mangle]
21796pub unsafe extern "C" fn mlockall(flags: i32) -> i32 {
21797    syscall_ret(<Arch as Syscalls>::syscall1(sysno(151, 230), flags as i64)) as i32
21798}
21799
21800#[no_mangle]
21801pub unsafe extern "C" fn munlockall() -> i32 {
21802    syscall_ret(<Arch as Syscalls>::syscall0(sysno(152, 231))) as i32
21803}
21804
21805#[no_mangle]
21806pub unsafe extern "C" fn madvise(addr: *mut u8, len: usize, advice: i32) -> i32 {
21807    syscall_ret(<Arch as Syscalls>::syscall3(sysno(28, 233), addr as i64, len as i64, advice as i64)) as i32
21808}
21809
21810#[no_mangle]
21811pub unsafe extern "C" fn mincore(addr: *mut u8, len: usize, vec: *mut u8) -> i32 {
21812    syscall_ret(<Arch as Syscalls>::syscall3(sysno(27, 232), addr as i64, len as i64, vec as i64)) as i32
21813}
21814
21815// ============================================================
21816
21817// ============================================================
21818// Missing math functions
21819// ============================================================
21820
21821#[no_mangle]
21822pub unsafe extern "C" fn drem(x: f64, y: f64) -> f64 {
21823    remainder(x, y)
21824}
21825
21826#[no_mangle]
21827pub unsafe extern "C" fn dremf(x: f32, y: f32) -> f32 {
21828    remainderf(x, y)
21829}
21830
21831#[no_mangle]
21832pub unsafe extern "C" fn pow10(x: f64) -> f64 {
21833    libm::pow(10.0, x)
21834}
21835
21836#[no_mangle]
21837pub unsafe extern "C" fn pow10f(x: f32) -> f32 {
21838    libm::powf(10.0, x)
21839}
21840
21841#[no_mangle]
21842pub unsafe extern "C" fn pow10l(x: f64) -> f64 {
21843    libm::pow(10.0, x)
21844}
21845
21846#[no_mangle]
21847pub unsafe extern "C" fn isless(x: f64, y: f64) -> i32 {
21848    if x.is_nan() || y.is_nan() { 0 } else { (x < y) as i32 }
21849}
21850
21851#[no_mangle]
21852pub unsafe extern "C" fn islessf(x: f32, y: f32) -> i32 {
21853    if x.is_nan() || y.is_nan() { 0 } else { (x < y) as i32 }
21854}
21855
21856#[no_mangle]
21857pub unsafe extern "C" fn lgamma_r(x: f64, signgamp: *mut i32) -> f64 {
21858    lgamma_r_impl(x, signgamp)
21859}
21860
21861#[no_mangle]
21862pub unsafe extern "C" fn lgammaf_r(x: f32, signgamp: *mut i32) -> f32 {
21863    if x.is_nan() {
21864        if !signgamp.is_null() { *signgamp = 1; }
21865        return x;
21866    }
21867    if x == 0.0 {
21868        if !signgamp.is_null() { *signgamp = if x.is_sign_negative() { -1 } else { 1 }; }
21869        feraiseexcept(FE_DIVBYZERO);
21870        return f32::INFINITY;
21871    }
21872    let (r, sign) = libm::lgammaf_r(x);
21873    if !signgamp.is_null() { *signgamp = sign; }
21874    r
21875}
21876
21877#[no_mangle]
21878pub unsafe extern "C" fn lgammal_r(x: f64, signgamp: *mut i32) -> f64 {
21879    lgamma_r(x, signgamp)
21880}
21881
21882// ============================================================
21883// Comparison macros
21884// ============================================================
21885
21886#[no_mangle]
21887pub unsafe extern "C" fn islessequal(x: f64, y: f64) -> i32 {
21888    if x.is_nan() || y.is_nan() { 0 } else { (x <= y) as i32 }
21889}
21890
21891#[no_mangle]
21892pub unsafe extern "C" fn islessequalf(x: f32, y: f32) -> i32 {
21893    if x.is_nan() || y.is_nan() { 0 } else { (x <= y) as i32 }
21894}
21895
21896#[no_mangle]
21897pub unsafe extern "C" fn islessgreater(x: f64, y: f64) -> i32 {
21898    if x.is_nan() || y.is_nan() { 0 } else { (x < y || x > y) as i32 }
21899}
21900
21901#[no_mangle]
21902pub unsafe extern "C" fn islessgreaterf(x: f32, y: f32) -> i32 {
21903    if x.is_nan() || y.is_nan() { 0 } else { (x < y || x > y) as i32 }
21904}
21905
21906#[no_mangle]
21907pub unsafe extern "C" fn isgreater(x: f64, y: f64) -> i32 {
21908    if x.is_nan() || y.is_nan() { 0 } else { (x > y) as i32 }
21909}
21910
21911#[no_mangle]
21912pub unsafe extern "C" fn isgreaterf(x: f32, y: f32) -> i32 {
21913    if x.is_nan() || y.is_nan() { 0 } else { (x > y) as i32 }
21914}
21915
21916#[no_mangle]
21917pub unsafe extern "C" fn isgreaterequal(x: f64, y: f64) -> i32 {
21918    if x.is_nan() || y.is_nan() { 0 } else { (x >= y) as i32 }
21919}
21920
21921#[no_mangle]
21922pub unsafe extern "C" fn isgreaterequalf(x: f32, y: f32) -> i32 {
21923    if x.is_nan() || y.is_nan() { 0 } else { (x >= y) as i32 }
21924}
21925
21926// ============================================================
21927// musl ABI compatibility helpers
21928// ============================================================
21929
21930#[no_mangle]
21931pub unsafe extern "C" fn stpcpy(dest: *mut c_char, src: *const c_char) -> *mut c_char {
21932    let mut d = dest;
21933    let mut s = src;
21934    while *s != 0 {
21935        *d = *s;
21936        d = d.add(1);
21937        s = s.add(1);
21938    }
21939    *d = 0;
21940    d
21941}
21942
21943#[no_mangle]
21944pub unsafe extern "C" fn stpncpy(dest: *mut c_char, src: *const c_char, n: usize) -> *mut c_char {
21945    let mut i = 0;
21946    while i < n && *src.add(i) != 0 {
21947        *dest.add(i) = *src.add(i);
21948        i += 1;
21949    }
21950    let end = dest.add(i);
21951    while i < n {
21952        *dest.add(i) = 0;
21953        i += 1;
21954    }
21955    end
21956}
21957
21958#[no_mangle]
21959pub unsafe extern "C" fn strchrnul(s: *const c_char, c: c_int) -> *mut c_char {
21960    let mut p = s;
21961    while *p != 0 && *p as c_int != c {
21962        p = p.add(1);
21963    }
21964    p as *mut c_char
21965}
21966
21967#[no_mangle]
21968pub unsafe extern "C" fn fstatat(dirfd: c_int, path: *const c_char, buf: *mut Stat, flags: c_int) -> c_int {
21969    let r = sys_newfstatat(dirfd, path, buf as *mut u8, flags);
21970    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
21971}
21972
21973#[no_mangle]
21974pub unsafe extern "C" fn execvpe(
21975    file: *const c_char,
21976    argv: *const *const c_char,
21977    envp: *const *const c_char,
21978) -> c_int {
21979    ERRNO!() = ENOENT_VAL;
21980    if file.is_null() || *file == 0 {
21981        return -1;
21982    }
21983    let mut p = file;
21984    while *p != 0 {
21985        if *p == b'/' as c_char {
21986            return execve(file, argv, envp);
21987        }
21988        p = p.add(1);
21989    }
21990    let file_len = crate::strlen(file);
21991    let path = crate::getenv(b"PATH\0".as_ptr() as *const c_char);
21992    let default_path = b"/usr/local/bin:/bin:/usr/bin\0";
21993    let path = if path.is_null() { default_path.as_ptr() as *const c_char } else { path };
21994
21995    let mut seen_eacces = false;
21996    let mut buf = [0 as c_char; 4096];
21997    let mut dir = path;
21998    loop {
21999        let mut end = dir;
22000        while *end != 0 && *end != b':' as c_char { end = end.add(1); }
22001        let dlen = end.offset_from(dir) as usize;
22002        // An empty PATH component means the current directory (no slash).
22003        let need_slash = dlen > 0;
22004        let total = dlen + need_slash as usize + file_len;
22005        if total < buf.len() {
22006            let mut i = 0;
22007            while i < dlen { buf[i] = *dir.add(i); i += 1; }
22008            if need_slash { buf[i] = b'/' as c_char; i += 1; }
22009            let mut j = 0;
22010            while j < file_len { buf[i + j] = *file.add(j); j += 1; }
22011            buf[i + j] = 0;
22012            execve(buf.as_ptr(), argv, envp);
22013            match ERRNO!() {
22014                13 => seen_eacces = true, // EACCES
22015                2 | 20 => {}              // ENOENT, ENOTDIR: try next
22016                _ => return -1,
22017            }
22018        }
22019        if *end == 0 { break; }
22020        dir = end.add(1);
22021    }
22022    ERRNO!() = if seen_eacces { 13 } else { 2 };
22023    -1
22024}
22025
22026// Locale-aware entry points. crabc currently has a single global locale,
22027// so these are source/ABI-compatible wrappers around the normal functions.
22028#[no_mangle]
22029pub unsafe extern "C" fn strcoll_l(s1: *const c_char, s2: *const c_char, _loc: locale_t) -> c_int {
22030    strcoll(s1, s2)
22031}
22032
22033#[no_mangle]
22034pub unsafe extern "C" fn strxfrm_l(dest: *mut c_char, src: *const c_char, n: usize, _loc: locale_t) -> usize {
22035    strxfrm(dest, src, n)
22036}
22037
22038#[no_mangle]
22039pub unsafe extern "C" fn strerror_l(errnum: c_int, _loc: locale_t) -> *mut c_char {
22040    strerror(errnum)
22041}
22042
22043#[no_mangle]
22044pub unsafe extern "C" fn strcasecmp_l(s1: *const c_char, s2: *const c_char, _loc: locale_t) -> c_int {
22045    strcasecmp(s1, s2)
22046}
22047
22048#[no_mangle]
22049pub unsafe extern "C" fn strncasecmp_l(s1: *const c_char, s2: *const c_char, n: usize, _loc: locale_t) -> c_int {
22050    strncasecmp(s1, s2, n)
22051}
22052
22053#[no_mangle]
22054pub unsafe extern "C" fn strftime_l(s: *mut c_char, max: usize, fmt: *const c_char, tm: *const tm, _loc: locale_t) -> usize {
22055    strftime(s, max, fmt, tm)
22056}
22057
22058#[no_mangle]
22059pub unsafe extern "C" fn nl_langinfo_l(item: c_int, _loc: locale_t) -> *mut c_char {
22060    nl_langinfo(item)
22061}
22062
22063#[no_mangle]
22064pub unsafe extern "C" fn wcscoll_l(s1: *const u32, s2: *const u32, _loc: locale_t) -> c_int {
22065    wcscoll(s1, s2)
22066}
22067
22068#[no_mangle]
22069pub unsafe extern "C" fn wcsxfrm_l(dst: *mut wchar_t, src: *const wchar_t, n: usize, _loc: locale_t) -> usize {
22070    wcsxfrm(dst, src, n)
22071}
22072
22073#[no_mangle]
22074pub unsafe extern "C" fn wcsftime_l(s: *mut u32, max: usize, fmt: *const u32, tm: *const c_void, _loc: locale_t) -> usize {
22075    wcsftime(s, max, fmt, tm)
22076}
22077
22078#[no_mangle]
22079pub unsafe extern "C" fn wctype_l(name: *const c_char, _loc: locale_t) -> wctype_t {
22080    wctype(name)
22081}
22082
22083#[no_mangle]
22084pub unsafe extern "C" fn wctrans_l(name: *const c_char, _loc: locale_t) -> wctrans_t {
22085    wctrans(name)
22086}
22087
22088#[no_mangle]
22089pub unsafe extern "C" fn towlower_l(c: wint_t, _loc: locale_t) -> wint_t {
22090    towlower(c)
22091}
22092
22093#[no_mangle]
22094pub unsafe extern "C" fn towupper_l(c: wint_t, _loc: locale_t) -> wint_t {
22095    towupper(c)
22096}
22097
22098#[no_mangle]
22099pub unsafe extern "C" fn towctrans_l(c: wint_t, desc: wctrans_t, _loc: locale_t) -> wint_t {
22100    towctrans(c, desc)
22101}
22102
22103#[no_mangle] pub unsafe extern "C" fn iswalnum_l(c: wint_t, _l: locale_t) -> c_int { iswalnum(c) }
22104#[no_mangle] pub unsafe extern "C" fn iswalpha_l(c: wint_t, _l: locale_t) -> c_int { iswalpha(c) }
22105#[no_mangle] pub unsafe extern "C" fn iswblank_l(c: wint_t, _l: locale_t) -> c_int { iswblank(c) }
22106#[no_mangle] pub unsafe extern "C" fn iswcntrl_l(c: wint_t, _l: locale_t) -> c_int { iswcntrl(c) }
22107#[no_mangle] pub unsafe extern "C" fn iswdigit_l(c: wint_t, _l: locale_t) -> c_int { iswdigit(c) }
22108#[no_mangle] pub unsafe extern "C" fn iswgraph_l(c: wint_t, _l: locale_t) -> c_int { iswgraph(c) }
22109#[no_mangle] pub unsafe extern "C" fn iswlower_l(c: wint_t, _l: locale_t) -> c_int { iswlower(c) }
22110#[no_mangle] pub unsafe extern "C" fn iswprint_l(c: wint_t, _l: locale_t) -> c_int { iswprint(c) }
22111#[no_mangle] pub unsafe extern "C" fn iswpunct_l(c: wint_t, _l: locale_t) -> c_int { iswpunct(c) }
22112#[no_mangle] pub unsafe extern "C" fn iswspace_l(c: wint_t, _l: locale_t) -> c_int { iswspace(c) }
22113#[no_mangle] pub unsafe extern "C" fn iswupper_l(c: wint_t, _l: locale_t) -> c_int { iswupper(c) }
22114#[no_mangle] pub unsafe extern "C" fn iswxdigit_l(c: wint_t, _l: locale_t) -> c_int { iswxdigit(c) }
22115#[no_mangle] pub unsafe extern "C" fn iswctype_l(c: wint_t, desc: wctype_t, _l: locale_t) -> c_int { iswctype(c, desc) }
22116
22117// ISO C99 scanf ABI aliases expected by musl binaries.
22118#[no_mangle] pub unsafe extern "C" fn __isoc99_scanf(fmt: *const c_char, args: ...) -> c_int { vfscanf(stdin, fmt, args) }
22119#[no_mangle] pub unsafe extern "C" fn __isoc99_fscanf(f: *mut FILE, fmt: *const c_char, args: ...) -> c_int { vfscanf(f, fmt, args) }
22120#[no_mangle] pub unsafe extern "C" fn __isoc99_sscanf(s: *const c_char, fmt: *const c_char, args: ...) -> c_int { vsscanf(s, fmt, args) }
22121#[no_mangle] pub unsafe extern "C" fn __isoc99_vscanf(fmt: *const c_char, args: VaList) -> c_int { vfscanf(stdin, fmt, args) }
22122#[no_mangle] pub unsafe extern "C" fn __isoc99_vfscanf(f: *mut FILE, fmt: *const c_char, args: VaList) -> c_int { vfscanf(f, fmt, args) }
22123#[no_mangle] pub unsafe extern "C" fn __isoc99_vsscanf(s: *const c_char, fmt: *const c_char, args: VaList) -> c_int { vsscanf(s, fmt, args) }
22124#[no_mangle] pub unsafe extern "C" fn __isoc99_wscanf(fmt: *const wchar_t, args: ...) -> c_int { vfwscanf(stdin, fmt, args) }
22125#[no_mangle] pub unsafe extern "C" fn __isoc99_fwscanf(f: *mut FILE, fmt: *const wchar_t, args: ...) -> c_int { vfwscanf(f, fmt, args) }
22126#[no_mangle] pub unsafe extern "C" fn __isoc99_swscanf(s: *const wchar_t, fmt: *const wchar_t, args: ...) -> c_int { vswscanf(s, fmt, args) }
22127#[no_mangle] pub unsafe extern "C" fn __isoc99_vwscanf(fmt: *const wchar_t, args: VaList) -> c_int { vfwscanf(stdin, fmt, args) }
22128#[no_mangle] pub unsafe extern "C" fn __isoc99_vfwscanf(f: *mut FILE, fmt: *const wchar_t, args: VaList) -> c_int { vfwscanf(f, fmt, args) }
22129#[no_mangle] pub unsafe extern "C" fn __isoc99_vswscanf(s: *const wchar_t, fmt: *const wchar_t, args: VaList) -> c_int { vswscanf(s, fmt, args) }
22130
22131// Unlocked stdio entry points. The current stdio implementation is not
22132// thread-safe anyway, so these are aliases for the normal entry points.
22133#[no_mangle] pub unsafe extern "C" fn clearerr_unlocked(f: *mut FILE) { clearerr(f) }
22134#[no_mangle] pub unsafe extern "C" fn feof_unlocked(f: *mut FILE) -> c_int { feof(f) }
22135#[no_mangle] pub unsafe extern "C" fn ferror_unlocked(f: *mut FILE) -> c_int { ferror(f) }
22136#[no_mangle] pub unsafe extern "C" fn fileno_unlocked(f: *mut FILE) -> c_int { fileno(f) }
22137#[no_mangle] pub unsafe extern "C" fn fflush_unlocked(f: *mut FILE) -> c_int { fflush(f) }
22138#[no_mangle] pub unsafe extern "C" fn fgetc_unlocked(f: *mut FILE) -> c_int { fgetc(f) }
22139#[no_mangle] pub unsafe extern "C" fn fgets_unlocked(s: *mut c_char, n: c_int, f: *mut FILE) -> *mut c_char { fgets(s, n, f) }
22140#[no_mangle] pub unsafe extern "C" fn fputc_unlocked(c: c_int, f: *mut FILE) -> c_int { fputc(c, f) }
22141#[no_mangle] pub unsafe extern "C" fn fputs_unlocked(s: *const c_char, f: *mut FILE) -> c_int { fputs(s, f) }
22142#[no_mangle] pub unsafe extern "C" fn fread_unlocked(p: *mut c_void, size: usize, n: usize, f: *mut FILE) -> usize { fread(p, size, n, f) }
22143#[no_mangle] pub unsafe extern "C" fn fwrite_unlocked(p: *const c_void, size: usize, n: usize, f: *mut FILE) -> usize { fwrite(p, size, n, f) }
22144#[no_mangle] pub unsafe extern "C" fn fgetwc_unlocked(f: *mut FILE) -> wint_t { fgetwc(f) }
22145#[no_mangle] pub unsafe extern "C" fn fgetws_unlocked(s: *mut u32, n: c_int, f: *mut c_void) -> *mut u32 { fgetws(s, n, f) }
22146#[no_mangle] pub unsafe extern "C" fn fputwc_unlocked(c: wchar_t, f: *mut FILE) -> wint_t { fputwc(c, f) }
22147#[no_mangle] pub unsafe extern "C" fn fputws_unlocked(s: *const wchar_t, f: *mut FILE) -> c_int { fputws(s, f) }
22148#[no_mangle] pub unsafe extern "C" fn getwc_unlocked(f: *mut c_void) -> wint_t { getwc(f) }
22149#[no_mangle] pub unsafe extern "C" fn putwc_unlocked(c: u32, f: *mut c_void) -> wint_t { putwc(c, f) }
22150#[no_mangle] pub unsafe extern "C" fn getwchar_unlocked() -> wint_t { getwchar() }
22151#[no_mangle] pub unsafe extern "C" fn putwchar_unlocked(c: wchar_t) -> wint_t { putwchar(c) }
22152#[no_mangle] pub unsafe extern "C" fn fpurge(f: *mut FILE) { clearerr(f) }
22153#[no_mangle] pub unsafe extern "C" fn _IO_getc(f: *mut FILE) -> c_int { getc(f) }
22154#[no_mangle] pub unsafe extern "C" fn _IO_putc(c: c_int, f: *mut FILE) -> c_int { putc(c, f) }
22155#[no_mangle] pub unsafe extern "C" fn _IO_getc_unlocked(f: *mut FILE) -> c_int { getc(f) }
22156#[no_mangle] pub unsafe extern "C" fn _IO_putc_unlocked(c: c_int, f: *mut FILE) -> c_int { putc(c, f) }
22157#[no_mangle] pub unsafe extern "C" fn _IO_feof_unlocked(f: *mut FILE) -> c_int { feof(f) }
22158#[no_mangle] pub unsafe extern "C" fn _IO_ferror_unlocked(f: *mut FILE) -> c_int { ferror(f) }
22159
22160// ABI aliases used by musl and older C libraries.
22161#[no_mangle] pub unsafe extern "C" fn __getauxval(t: c_ulong) -> c_ulong { getauxval(t) }
22162#[no_mangle] pub unsafe extern "C" fn __longjmp(env: *const c_ulong, val: c_int) -> ! { longjmp(env, val) }
22163#[no_mangle] pub unsafe extern "C" fn bsd_signal(sig: c_int, handler: usize) -> usize { signal(sig, handler) }
22164#[no_mangle] pub unsafe extern "C" fn __sysv_signal(sig: c_int, handler: usize) -> usize { signal(sig, handler) }
22165#[no_mangle] pub unsafe extern "C" fn eaccess(path: *const c_char, mode: c_int) -> c_int { access(path, mode) }
22166#[no_mangle] pub unsafe extern "C" fn __xpg_basename(path: *mut c_char) -> *mut c_char { basename(path) }
22167#[no_mangle] pub unsafe extern "C" fn __xpg_strerror_r(errnum: c_int, buf: *mut c_char, len: usize) -> c_int { strerror_r(errnum, buf, len) }
22168#[no_mangle] pub unsafe extern "C" fn __posix_getopt(argc: c_int, argv: *const *const c_char, optstring: *const c_char) -> c_int { getopt(argc, argv, optstring) }
22169
22170// ============================================================
22171// Remaining musl ABI entry points
22172// ============================================================
22173
22174#[no_mangle]
22175pub unsafe extern "C" fn cfsetspeed(termios_p: *mut c_void, speed: u32) -> c_int {
22176    let r = cfsetispeed(termios_p, speed);
22177    if r != 0 { return r; }
22178    cfsetospeed(termios_p, speed)
22179}
22180
22181#[no_mangle]
22182pub unsafe extern "C" fn futimesat(dirfd: c_int, path: *const c_char, times: *const timeval) -> c_int {
22183    if times.is_null() {
22184        return utimensat(dirfd, path, core::ptr::null(), 0);
22185    }
22186    let tv = times as *const [timeval; 2];
22187    let mut ts: [timespec; 2] = [
22188        timespec { tv_sec: (*tv)[0].tv_sec, tv_nsec: (*tv)[0].tv_usec * 1000 },
22189        timespec { tv_sec: (*tv)[1].tv_sec, tv_nsec: (*tv)[1].tv_usec * 1000 },
22190    ];
22191    for t in &mut ts {
22192        if t.tv_nsec < 0 || t.tv_nsec >= 1_000_000_000 {
22193            ERRNO!() = EINVAL;
22194            return -1;
22195        }
22196    }
22197    utimensat(dirfd, path, ts.as_ptr(), 0)
22198}
22199
22200#[no_mangle]
22201pub unsafe extern "C" fn membarrier(cmd: c_int, flags: c_int, cpu_id: c_int) -> c_int {
22202    <Arch as Syscalls>::syscall3(SYS_MEMBARRIER, cmd as i64, flags as i64, cpu_id as i64) as c_int
22203}
22204
22205#[no_mangle]
22206pub unsafe extern "C" fn mremap(
22207    old_address: *mut c_void,
22208    old_size: usize,
22209    new_size: usize,
22210    flags: c_int,
22211    mut args: ...
22212) -> *mut c_void {
22213    let new_address = if flags & 2 != 0 { args.next_arg::<*mut c_void>() } else { core::ptr::null_mut() };
22214    let r = <Arch as Syscalls>::syscall5(
22215        SYS_MREMAP,
22216        old_address as i64,
22217        old_size as i64,
22218        new_size as i64,
22219        flags as i64,
22220        new_address as i64,
22221    );
22222    if r < 0 {
22223        ERRNO!() = (-r) as c_int;
22224        return (-1isize) as *mut c_void;
22225    }
22226    r as *mut c_void
22227}
22228
22229#[no_mangle]
22230pub unsafe extern "C" fn ptsname_r(fd: c_int, buf: *mut c_char, buflen: usize) -> c_int {
22231    let name = ptsname(fd);
22232    if name.is_null() { return -1; }
22233    let n = crate::strlen(name);
22234    if n + 1 > buflen {
22235        ERRNO!() = ERANGE_VAL;
22236        return ERANGE_VAL;
22237    }
22238    core::ptr::copy_nonoverlapping(name, buf, n + 1);
22239    0
22240}
22241
22242#[no_mangle]
22243pub unsafe extern "C" fn posix_getdents(fd: c_int, buf: *mut c_void, len: usize, flags: c_int) -> isize {
22244    if flags != 0 {
22245        ERRNO!() = EINVAL;
22246        return -1;
22247    }
22248    let r = <Arch as Syscalls>::syscall3(SYS_GETDENTS64, fd as i64, buf as i64, len as i64);
22249    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { r as isize }
22250}
22251
22252#[no_mangle]
22253pub unsafe extern "C" fn riscv_flush_icache(start: *mut c_void, end: *mut c_void, flags: c_ulong) -> c_int {
22254    #[cfg(target_arch = "riscv64")]
22255    {
22256        let r = <Arch as Syscalls>::syscall3(259, start as i64, end as i64, flags as i64);
22257        if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
22258    }
22259    #[cfg(not(target_arch = "riscv64"))]
22260    {
22261        let _ = (start, end, flags);
22262        0
22263    }
22264}
22265
22266#[no_mangle]
22267pub unsafe extern "C" fn __riscv_flush_icache(start: *mut c_void, end: *mut c_void, flags: c_ulong) -> c_int {
22268    riscv_flush_icache(start, end, flags)
22269}
22270
22271#[no_mangle]
22272pub unsafe extern "C" fn mkostemps(template: *mut c_char, suffixlen: c_int, flags: c_int) -> c_int {
22273    let fd = mkstemps(template, suffixlen);
22274    if fd >= 0 && flags & O_CLOEXEC != 0 {
22275        sys_fcntl(fd, F_SETFD, FD_CLOEXEC as i64);
22276    }
22277    fd
22278}
22279
22280#[no_mangle]
22281pub unsafe extern "C" fn statfs(path: *const c_char, buf: *mut c_void) -> c_int {
22282    let r = <Arch as Syscalls>::syscall2(SYS_STATFS, path as i64, buf as i64);
22283    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
22284}
22285
22286#[no_mangle]
22287pub unsafe extern "C" fn fstatfs(fd: c_int, buf: *mut c_void) -> c_int {
22288    let r = <Arch as Syscalls>::syscall2(SYS_FSTATFS, fd as i64, buf as i64);
22289    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
22290}
22291
22292#[no_mangle]
22293pub unsafe extern "C" fn sysinfo(info: *mut c_void) -> c_int {
22294    let r = <Arch as Syscalls>::syscall1(SYS_SYSINFO, info as i64);
22295    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
22296}
22297
22298#[no_mangle]
22299pub unsafe extern "C" fn pthread_tryjoin_np(thread: PthreadT, retval: *mut *mut c_void) -> c_int {
22300    let slot = thread as *mut Thread;
22301    if slot.is_null() { return EINVAL; }
22302    if (*slot).detach_state == DT_DETACHED { return EINVAL; }
22303    let tid = core::ptr::read_volatile(&raw const (*slot).tid);
22304    if tid > 0 { return EBUSY; }
22305    if tid < 0 { return EINVAL; }
22306    pthread_join(thread, retval)
22307}
22308
22309#[no_mangle]
22310pub unsafe extern "C" fn pthread_timedjoin_np(thread: PthreadT, retval: *mut *mut c_void, abstime: *const timespec) -> c_int {
22311    let slot = thread as *mut Thread;
22312    if slot.is_null() { return EINVAL; }
22313    if (*slot).detach_state == DT_DETACHED { return EINVAL; }
22314    loop {
22315        let r = pthread_tryjoin_np(thread, retval);
22316        if r != EBUSY { return r; }
22317        let tid = core::ptr::read_volatile(&raw const (*slot).tid);
22318        if tid <= 0 { continue; }
22319        let e = futex_timedwait(&raw mut (*slot).tid, tid, abstime);
22320        if e == ETIMEDOUT { return ETIMEDOUT; }
22321    }
22322}
22323
22324
22325// utmp/utmpx are backed by the classic /var/run/utmp file.  musl itself
22326// leaves these as no-ops; crabc provides the real file-backed API.
22327#[repr(C)]
22328pub struct UtmpExit {
22329    pub e_termination: i16,
22330    pub e_exit: i16,
22331}
22332
22333#[repr(C)]
22334pub struct Utmpx {
22335    pub ut_type: i16,
22336    pub __ut_pad1: i16,
22337    pub ut_pid: i32,
22338    pub ut_line: [c_char; 32],
22339    pub ut_id: [c_char; 4],
22340    pub ut_user: [c_char; 32],
22341    pub ut_host: [c_char; 256],
22342    pub ut_exit: UtmpExit,
22343    pub ut_session: i32,
22344    pub __ut_pad2: i32,
22345    pub ut_tv: timeval,
22346    pub ut_addr_v6: [u32; 4],
22347    pub __unused: [u8; 20],
22348}
22349
22350const UTMP_SIZE: usize = 400;
22351const _: [(); UTMP_SIZE] = [(); core::mem::size_of::<Utmpx>()];
22352
22353static mut UTMP_FD: c_int = -1;
22354static mut UTMP_PATH: [u8; 256] = [0; 256];
22355static mut UTMP_PATH_SET: bool = false;
22356#[repr(align(8))]
22357struct UtmpBuf([u8; UTMP_SIZE]);
22358static mut UTMP_BUF: UtmpBuf = UtmpBuf([0; UTMP_SIZE]);
22359
22360unsafe fn utmp_path_ptr() -> *const c_char {
22361    if !UTMP_PATH_SET {
22362        let default = b"/var/run/utmp\0";
22363        let mut i = 0;
22364        while i < default.len() && i < UTMP_PATH.len() {
22365            UTMP_PATH[i] = default[i];
22366            i += 1;
22367        }
22368        UTMP_PATH_SET = true;
22369    }
22370    UTMP_PATH.as_ptr() as *const c_char
22371}
22372
22373unsafe fn utmp_open_read() -> c_int {
22374    if UTMP_FD >= 0 {
22375        crate::close(UTMP_FD);
22376    }
22377    UTMP_FD = crate::open(utmp_path_ptr(), 0, 0);
22378    UTMP_FD
22379}
22380
22381unsafe fn utmp_open_append() -> c_int {
22382    crate::open(utmp_path_ptr(), 1 | 64 | 1024, 0o644)
22383}
22384
22385#[no_mangle]
22386pub unsafe extern "C" fn setutent() {
22387    let _ = utmp_open_read();
22388}
22389
22390#[no_mangle]
22391pub unsafe extern "C" fn endutent() {
22392    if UTMP_FD >= 0 {
22393        crate::close(UTMP_FD);
22394        UTMP_FD = -1;
22395    }
22396}
22397
22398#[no_mangle]
22399pub unsafe extern "C" fn getutent() -> *mut c_void {
22400    if UTMP_FD < 0 && utmp_open_read() < 0 {
22401        return core::ptr::null_mut();
22402    }
22403    let n = crate::read(UTMP_FD, UTMP_BUF.0.as_mut_ptr() as *mut c_void, UTMP_SIZE);
22404    if n == UTMP_SIZE as isize {
22405        UTMP_BUF.0.as_mut_ptr() as *mut c_void
22406    } else {
22407        core::ptr::null_mut()
22408    }
22409}
22410
22411unsafe fn utmp_id_eq(a: &[c_char; 4], b: &[c_char; 4]) -> bool {
22412    a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3]
22413}
22414
22415unsafe fn utmp_line_eq(a: &[c_char; 32], b: &[c_char; 32]) -> bool {
22416    let mut i = 0;
22417    while i < 32 {
22418        if a[i] != b[i] { return false; }
22419        if a[i] == 0 { return true; }
22420        i += 1;
22421    }
22422    true
22423}
22424
22425#[no_mangle]
22426pub unsafe extern "C" fn getutid(ut: *const c_void) -> *mut c_void {
22427    if ut.is_null() { return core::ptr::null_mut(); }
22428    let want = &*(ut as *const Utmpx);
22429    loop {
22430        let p = getutent();
22431        if p.is_null() { return p; }
22432        let cur = &*(p as *const Utmpx);
22433        let matched = match want.ut_type {
22434            1 | 2 | 3 | 4 => cur.ut_type == want.ut_type,
22435            _ => utmp_id_eq(&cur.ut_id, &want.ut_id)
22436                && (want.ut_type == 0 || cur.ut_type == want.ut_type),
22437        };
22438        if matched { return p; }
22439    }
22440}
22441
22442#[no_mangle]
22443pub unsafe extern "C" fn getutline(ut: *const c_void) -> *mut c_void {
22444    if ut.is_null() { return core::ptr::null_mut(); }
22445    let want = &*(ut as *const Utmpx);
22446    loop {
22447        let p = getutent();
22448        if p.is_null() { return p; }
22449        if utmp_line_eq(&(*(p as *const Utmpx)).ut_line, &want.ut_line) {
22450            return p;
22451        }
22452    }
22453}
22454
22455#[no_mangle]
22456pub unsafe extern "C" fn pututline(ut: *const c_void) -> *mut c_void {
22457    if ut.is_null() { return core::ptr::null_mut(); }
22458    let fd = utmp_open_append();
22459    if fd < 0 { return core::ptr::null_mut(); }
22460    let n = crate::write(fd, ut, UTMP_SIZE);
22461    crate::close(fd);
22462    if n == UTMP_SIZE as isize { ut as *mut c_void } else { core::ptr::null_mut() }
22463}
22464
22465#[no_mangle]
22466pub unsafe extern "C" fn updwtmp(file: *const c_char, ut: *const c_void) {
22467    if file.is_null() || ut.is_null() { return; }
22468    let fd = crate::open(file, 1 | 64 | 1024, 0o644);
22469    if fd >= 0 {
22470        let _ = crate::write(fd, ut, UTMP_SIZE);
22471        crate::close(fd);
22472    }
22473}
22474
22475unsafe fn utmp_set_name(file: *const c_char) -> c_int {
22476    if file.is_null() { return -1; }
22477    let len = crate::strlen(file);
22478    if len + 1 > UTMP_PATH.len() { return -1; }
22479    if UTMP_FD >= 0 {
22480        crate::close(UTMP_FD);
22481        UTMP_FD = -1;
22482    }
22483    core::ptr::copy_nonoverlapping(file, UTMP_PATH.as_mut_ptr() as *mut c_char, len + 1);
22484    UTMP_PATH_SET = true;
22485    0
22486}
22487
22488#[no_mangle]
22489pub unsafe extern "C" fn utmpname(file: *const c_char) -> c_int { utmp_set_name(file) }
22490
22491#[no_mangle]
22492pub unsafe extern "C" fn utmpxname(file: *const c_char) -> c_int { utmp_set_name(file) }
22493
22494
22495// Data symbols and internal aliases still referenced by some musl binaries.
22496#[no_mangle] pub static mut ___environ: *mut *mut c_char = core::ptr::null_mut();
22497#[no_mangle] pub static mut _environ: *mut *mut c_char = core::ptr::null_mut();
22498#[no_mangle] pub unsafe extern "C" fn __freelocale(loc: locale_t) { freelocale(loc) }
22499#[no_mangle] pub unsafe extern "C" fn __getdelim(lineptr: *mut *mut c_char, n: *mut usize, delim: c_int, stream: *mut FILE) -> isize { getdelim(lineptr, n, delim, stream) }
22500#[no_mangle] pub unsafe extern "C" fn __strtod_l(s: *const c_char, end: *mut *mut c_char, loc: locale_t) -> f64 { strtod_l(s, end, loc) }
22501#[no_mangle] pub unsafe extern "C" fn __strtof_l(s: *const c_char, end: *mut *mut c_char, loc: locale_t) -> f32 { strtof_l(s, end, loc) }
22502#[no_mangle] pub unsafe extern "C" fn __strtold_l(s: *const c_char, end: *mut *mut c_char, loc: locale_t) -> f64 { strtold_l(s, end, loc) }
22503
22504#[no_mangle] pub unsafe extern "C" fn __strtol_internal(s: *const c_char, end: *mut *mut c_char, base: c_int) -> c_long { strtol(s, end, base) }
22505#[no_mangle] pub unsafe extern "C" fn __strtoul_internal(s: *const c_char, end: *mut *mut c_char, base: c_int) -> c_ulong { strtoul(s, end, base) }
22506#[no_mangle] pub unsafe extern "C" fn __strtoll_internal(s: *const c_char, end: *mut *mut c_char, base: c_int) -> c_longlong { strtoll(s, end, base) }
22507#[no_mangle] pub unsafe extern "C" fn __strtoull_internal(s: *const c_char, end: *mut *mut c_char, base: c_int) -> c_ulonglong { strtoull(s, end, base) }
22508#[no_mangle] pub unsafe extern "C" fn __strtoimax_internal(s: *const c_char, end: *mut *mut c_char, base: c_int) -> i64 { strtoimax(s, end, base) }
22509#[no_mangle] pub unsafe extern "C" fn __strtoumax_internal(s: *const c_char, end: *mut *mut c_char, base: c_int) -> u64 { strtoumax(s, end, base) }
22510
22511// ============================================================
22512// Resolver (UDP DNS)
22513// ============================================================
22514
22515const MAX_NS: usize = 3;
22516const MAX_SEARCH: usize = 6;
22517const HOST_NOT_FOUND: c_int = 1;
22518const TRY_AGAIN: c_int = 2;
22519const NO_RECOVERY: c_int = 3;
22520const NO_DATA: c_int = 4;
22521
22522static mut NS_COUNT: usize = 0;
22523static mut NS_ADDRS: [u32; MAX_NS] = [0; MAX_NS];
22524static mut SEARCH_COUNT: usize = 0;
22525static mut SEARCH_DOMAINS: [[u8; 256]; MAX_SEARCH] = [[0; 256]; MAX_SEARCH];
22526static mut RESOLV_INITIALIZED: bool = false;
22527
22528#[repr(C)]
22529struct SockAddrIn {
22530    sin_family: u16,
22531    sin_port: u16,
22532    sin_addr: u32,
22533    sin_zero: [u8; 8],
22534}
22535
22536#[repr(C)]
22537struct PollFd {
22538    fd: c_int,
22539    events: i16,
22540    revents: i16,
22541}
22542
22543unsafe fn resolv_skip_ws(line: &[u8], mut i: usize) -> usize {
22544    while i < line.len() && (line[i] == b' ' || line[i] == b'\t') { i += 1; }
22545    i
22546}
22547
22548unsafe fn resolv_token<'a>(line: &'a [u8], mut i: usize) -> (&'a [u8], usize) {
22549    let start = i;
22550    while i < line.len() && line[i] != b' ' && line[i] != b'\t' && line[i] != b'#' { i += 1; }
22551    (&line[start..i], i)
22552}
22553
22554unsafe fn resolv_parse_config() -> c_int {
22555    RESOLV_INITIALIZED = true;
22556    NS_COUNT = 0;
22557    SEARCH_COUNT = 0;
22558    let path = b"/etc/resolv.conf\0";
22559    let fd = crate::open(path.as_ptr() as *const c_char, 0, 0);
22560    if fd < 0 { return -1; }
22561    let mut buf = [0u8; 4096];
22562    let n = crate::read(fd, buf.as_mut_ptr() as *mut c_void, buf.len());
22563    crate::close(fd);
22564    if n <= 0 { return -1; }
22565    let len = n as usize;
22566    let mut i = 0;
22567    while i < len {
22568        let start = i;
22569        while i < len && buf[i] != b'\n' { i += 1; }
22570        let line = &buf[start..i];
22571        i += 1;
22572        let mut j = resolv_skip_ws(line, 0);
22573        if j >= line.len() || line[j] == b'#' { continue; }
22574        let (key, next) = resolv_token(line, j);
22575        j = next;
22576        if key == b"nameserver" {
22577            j = resolv_skip_ws(line, j);
22578            let (tok, _) = resolv_token(line, j);
22579            if !tok.is_empty() && NS_COUNT < MAX_NS {
22580                let mut name = [0u8; 64];
22581                let l = core::cmp::min(tok.len(), 63);
22582                name[..l].copy_from_slice(&tok[..l]);
22583                let addr = crate::inet_addr(name.as_ptr() as *const c_char);
22584                if addr != u32::MAX {
22585                    NS_ADDRS[NS_COUNT] = addr;
22586                    NS_COUNT += 1;
22587                }
22588            }
22589        } else if key == b"search" || key == b"domain" {
22590            loop {
22591                j = resolv_skip_ws(line, j);
22592                if j >= line.len() || line[j] == b'#' { break; }
22593                let (tok, next) = resolv_token(line, j);
22594                j = next;
22595                if tok.is_empty() { break; }
22596                if SEARCH_COUNT < MAX_SEARCH {
22597                    let l = core::cmp::min(tok.len(), 255);
22598                    SEARCH_DOMAINS[SEARCH_COUNT][..l].copy_from_slice(&tok[..l]);
22599                    SEARCH_DOMAINS[SEARCH_COUNT][l] = 0;
22600                    SEARCH_COUNT += 1;
22601                }
22602                if key == b"domain" { break; }
22603            }
22604        }
22605    }
22606    0
22607}
22608
22609#[no_mangle]
22610pub unsafe extern "C" fn res_init() -> c_int {
22611    let _ = resolv_parse_config();
22612    0
22613}
22614
22615#[no_mangle]
22616pub unsafe extern "C" fn res_mkquery(
22617    op: c_int,
22618    dname: *const c_char,
22619    class: c_int,
22620    type_: c_int,
22621    _data: *const u8,
22622    _datalen: c_int,
22623    _newrr: *const u8,
22624    buf: *mut u8,
22625    buflen: c_int,
22626) -> c_int {
22627    if dname.is_null() || buf.is_null() { return -1; }
22628    let mut l = crate::strnlen(dname as *const u8, 255);
22629    if l > 0 && *dname.add(l - 1) == b'.' as c_char { l -= 1; }
22630    if l > 0 && *dname.add(l - 1) == b'.' as c_char { return -1; }
22631    let n = 17 + l + if l != 0 { 1 } else { 0 };
22632    if l > 253 || buflen < n as c_int || op < 0 || op > 15 || class < 0 || class > 255 || type_ < 0 || type_ > 255 {
22633        return -1;
22634    }
22635    let mut q = [0u8; 280];
22636    crate::memset(q.as_mut_ptr() as *mut c_void, 0, n);
22637    q[2] = (op * 8 + 1) as u8;
22638    q[3] = 32;
22639    q[5] = 1;
22640    core::ptr::copy_nonoverlapping(dname as *const u8, q.as_mut_ptr().add(13), l);
22641    let mut i = 13;
22642    while q[i] != 0 {
22643        let mut j = i;
22644        while q[j] != 0 && q[j] != b'.' { j += 1; }
22645        if j - i - 1 > 62 { return -1; }
22646        q[i - 1] = (j - i) as u8;
22647        i = j + 1;
22648    }
22649    q[i + 1] = type_ as u8;
22650    q[i + 3] = class as u8;
22651    let mut ts = timespec { tv_sec: 0, tv_nsec: 0 };
22652    let _ = clock_gettime(CLOCK_REALTIME, &mut ts);
22653    let id = (((ts.tv_nsec as u64) + (ts.tv_nsec as u64) / 65536) & 0xffff) as u16;
22654    q[0] = (id >> 8) as u8;
22655    q[1] = id as u8;
22656    core::ptr::copy_nonoverlapping(q.as_ptr(), buf, n);
22657    n as c_int
22658}
22659
22660unsafe fn res_send_inner(msg: *const u8, msglen: c_int, answer: *mut u8, anslen: c_int) -> c_int {
22661    if msg.is_null() || answer.is_null() || msglen < 12 || anslen < 12 { return -1; }
22662    if !RESOLV_INITIALIZED { let _ = resolv_parse_config(); }
22663    if NS_COUNT == 0 {
22664        h_errno = TRY_AGAIN;
22665        return -1;
22666    }
22667    for n in 0..NS_COUNT {
22668        let fd = crate::socket(AF_INET, SOCK_DGRAM, 0);
22669        if fd < 0 { continue; }
22670        let sa = SockAddrIn {
22671            sin_family: AF_INET as u16,
22672            sin_port: 53u16.to_be(),
22673            sin_addr: NS_ADDRS[n],
22674            sin_zero: [0; 8],
22675        };
22676        let sent = crate::sendto(fd, msg as *const c_void, msglen as usize, 0, &sa as *const SockAddrIn as *const crate::sockaddr, 16);
22677        if sent != msglen as isize {
22678            crate::close(fd);
22679            continue;
22680        }
22681        let mut pfd = PollFd { fd, events: 1, revents: 0 };
22682        let pr = crate::poll(&mut pfd as *mut PollFd as *mut c_void, 1, 5000);
22683        if pr <= 0 {
22684            crate::close(fd);
22685            continue;
22686        }
22687        let r = crate::recvfrom(fd, answer as *mut c_void, anslen as usize, 0, core::ptr::null_mut(), core::ptr::null_mut());
22688        crate::close(fd);
22689        if r >= 12 {
22690            let id_ok = *answer == *msg && *answer.add(1) == *msg.add(1);
22691            let qr = (*answer.add(2) & 0x80) != 0;
22692            if id_ok && qr {
22693                return r as c_int;
22694            }
22695        }
22696    }
22697    h_errno = TRY_AGAIN;
22698    -1
22699}
22700
22701#[no_mangle]
22702pub unsafe extern "C" fn res_send(msg: *const u8, msglen: c_int, answer: *mut u8, anslen: c_int) -> c_int {
22703    if anslen < 512 {
22704        let mut buf = [0u8; 512];
22705        let r = res_send_inner(msg, msglen, buf.as_mut_ptr(), 512);
22706        if r >= 0 {
22707            let copy = core::cmp::min(r, anslen) as usize;
22708            if copy > 0 { core::ptr::copy_nonoverlapping(buf.as_ptr(), answer, copy); }
22709        }
22710        return r;
22711    }
22712    res_send_inner(msg, msglen, answer, anslen)
22713}
22714
22715#[no_mangle]
22716pub unsafe extern "C" fn res_query(
22717    dname: *const c_char,
22718    class: c_int,
22719    type_: c_int,
22720    answer: *mut u8,
22721    anslen: c_int,
22722) -> c_int {
22723    let mut q = [0u8; 280];
22724    let ql = res_mkquery(0, dname, class, type_, core::ptr::null(), 0, core::ptr::null(), q.as_mut_ptr(), q.len() as c_int);
22725    if ql < 0 { return ql; }
22726    let r = res_send(q.as_ptr(), ql, answer, anslen);
22727    if r < 12 {
22728        h_errno = TRY_AGAIN;
22729        return -1;
22730    }
22731    let rcode = answer.add(3).read() & 0x0f;
22732    if rcode == 3 {
22733        h_errno = HOST_NOT_FOUND;
22734        return -1;
22735    }
22736    if rcode == 0 && *answer.add(6) == 0 && *answer.add(7) == 0 {
22737        h_errno = NO_DATA;
22738        return -1;
22739    }
22740    if rcode != 0 {
22741        h_errno = NO_RECOVERY;
22742        return -1;
22743    }
22744    r
22745}
22746
22747#[no_mangle]
22748pub unsafe extern "C" fn res_querydomain(
22749    name: *const c_char,
22750    domain: *const c_char,
22751    class: c_int,
22752    type_: c_int,
22753    answer: *mut u8,
22754    anslen: c_int,
22755) -> c_int {
22756    if domain.is_null() || *domain == 0 { return res_query(name, class, type_, answer, anslen); }
22757    let mut buf = [0u8; 512];
22758    let nl = crate::strlen(name);
22759    let dl = crate::strlen(domain);
22760    if nl + 1 + dl >= buf.len() { return -1; }
22761    core::ptr::copy_nonoverlapping(name, buf.as_mut_ptr() as *mut c_char, nl);
22762    buf[nl] = b'.';
22763    core::ptr::copy_nonoverlapping(domain, buf.as_mut_ptr().add(nl + 1) as *mut c_char, dl);
22764    buf[nl + 1 + dl] = 0;
22765    res_query(buf.as_ptr() as *const c_char, class, type_, answer, anslen)
22766}
22767
22768#[no_mangle]
22769pub unsafe extern "C" fn res_search(
22770    name: *const c_char,
22771    class: c_int,
22772    type_: c_int,
22773    answer: *mut u8,
22774    anslen: c_int,
22775) -> c_int {
22776    if name.is_null() { return -1; }
22777    if !RESOLV_INITIALIZED { let _ = resolv_parse_config(); }
22778    let len = crate::strlen(name);
22779    let mut dots = 0;
22780    for i in 0..len {
22781        if *name.add(i) == b'.' as c_char { dots += 1; }
22782    }
22783    let trailing_dot = len > 0 && *name.add(len - 1) == b'.' as c_char;
22784    if trailing_dot || dots >= 1 {
22785        let r = res_query(name, class, type_, answer, anslen);
22786        if r >= 0 { return r; }
22787        if trailing_dot { return -1; }
22788    }
22789    for i in 0..SEARCH_COUNT {
22790        let dom = SEARCH_DOMAINS[i].as_ptr() as *const c_char;
22791        let r = res_querydomain(name, dom, class, type_, answer, anslen);
22792        if r >= 0 { return r; }
22793        if h_errno != HOST_NOT_FOUND && h_errno != NO_DATA { return -1; }
22794    }
22795    if dots == 0 {
22796        return res_query(name, class, type_, answer, anslen);
22797    }
22798    -1
22799}
22800
22801// ============================================================
22802// Host and address resolution
22803// ============================================================
22804
22805#[repr(C)]
22806pub struct HostEnt {
22807    pub h_name: *mut c_char,
22808    pub h_aliases: *mut *mut c_char,
22809    pub h_addrtype: c_int,
22810    pub h_length: c_int,
22811    pub h_addr_list: *mut *mut c_char,
22812}
22813
22814#[repr(C)]
22815pub struct AddrInfo {
22816    pub ai_flags: c_int,
22817    pub ai_family: c_int,
22818    pub ai_socktype: c_int,
22819    pub ai_protocol: c_int,
22820    pub ai_addrlen: u32,
22821    pub ai_addr: *mut c_void,
22822    pub ai_canonname: *mut c_char,
22823    pub ai_next: *mut AddrInfo,
22824}
22825
22826static mut HOST_ENT: HostEnt = HostEnt {
22827    h_name: core::ptr::null_mut(),
22828    h_aliases: core::ptr::null_mut(),
22829    h_addrtype: 0,
22830    h_length: 0,
22831    h_addr_list: core::ptr::null_mut(),
22832};
22833static mut HOST_NAME: [c_char; 256] = [0; 256];
22834static mut HOST_ALIASES: [*mut c_char; 1] = [core::ptr::null_mut()];
22835static mut HOST_ADDR: [u8; 16] = [0; 16];
22836static mut HOST_ADDR_PTRS: [*mut c_char; 2] = [core::ptr::null_mut(); 2];
22837
22838unsafe fn dns_skip_name(p: *const u8, end: *const u8) -> *const u8 {
22839    let mut q = p;
22840    while q < end {
22841        let c = *q;
22842        if c == 0 { return q.add(1); }
22843        if c & 0xc0 == 0xc0 { return q.add(2); }
22844        q = q.add(1 + c as usize);
22845    }
22846    end
22847}
22848
22849unsafe fn dns_read_u16(p: *const u8) -> u16 {
22850    ((*p as u16) << 8) | *p.add(1) as u16
22851}
22852
22853unsafe fn dns_expand_name(p: *const u8, msg: *const u8, out: *mut c_char, outlen: usize) -> bool {
22854    let mut q = p;
22855    let mut o = 0usize;
22856    let mut jumps = 0;
22857    loop {
22858        let c = *q;
22859        if c == 0 {
22860            if o < outlen { *out.add(o) = 0; }
22861            return true;
22862        }
22863        if c & 0xc0 == 0xc0 {
22864            let off = (((c & 0x3f) as usize) << 8) | *q.add(1) as usize;
22865            q = msg.add(off);
22866            jumps += 1;
22867            if jumps > 16 { return false; }
22868            continue;
22869        }
22870        let l = c as usize;
22871        if l > 63 { return false; }
22872        if o + l + 1 >= outlen { return false; }
22873        core::ptr::copy_nonoverlapping(q.add(1), out.add(o) as *mut u8, l);
22874        o += l;
22875        *out.add(o) = b'.' as c_char;
22876        o += 1;
22877        q = q.add(1 + l);
22878    }
22879}
22880
22881unsafe fn host_from_answer(answer: *const u8, len: c_int, af: c_int) -> *mut HostEnt {
22882    if len < 12 { return core::ptr::null_mut(); }
22883    let end = answer.add(len as usize);
22884    let qd = dns_read_u16(answer.add(4)) as usize;
22885    let an = dns_read_u16(answer.add(6)) as usize;
22886    let mut p = answer.add(12);
22887    for _ in 0..qd {
22888        p = dns_skip_name(p, end);
22889        p = p.add(4);
22890    }
22891    let want_type: u16 = if af == AF_INET6 { 28 } else { 1 };
22892    let want_len = if af == AF_INET6 { 16 } else { 4 };
22893    for _ in 0..an {
22894        p = dns_skip_name(p, end);
22895        if p.add(10) > end { break; }
22896        let typ = dns_read_u16(p);
22897        let class = dns_read_u16(p.add(2));
22898        let rdlen = dns_read_u16(p.add(8)) as usize;
22899        let rdata = p.add(10);
22900        if rdata.add(rdlen) > end { break; }
22901        if typ == want_type && class == 1 && rdlen == want_len {
22902            core::ptr::copy_nonoverlapping(rdata, HOST_ADDR.as_mut_ptr(), want_len);
22903            HOST_ENT.h_addrtype = af;
22904            HOST_ENT.h_length = want_len as c_int;
22905            HOST_ADDR_PTRS[0] = HOST_ADDR.as_mut_ptr() as *mut c_char;
22906            HOST_ADDR_PTRS[1] = core::ptr::null_mut();
22907            HOST_ENT.h_addr_list = HOST_ADDR_PTRS.as_mut_ptr();
22908            HOST_ENT.h_aliases = HOST_ALIASES.as_mut_ptr();
22909            HOST_ENT.h_name = HOST_NAME.as_mut_ptr();
22910            h_errno = 0;
22911            return &raw mut HOST_ENT;
22912        }
22913        p = rdata.add(rdlen);
22914    }
22915    core::ptr::null_mut()
22916}
22917
22918#[no_mangle]
22919pub unsafe extern "C" fn gethostbyname(name: *const c_char) -> *mut HostEnt {
22920    gethostbyname2(name, AF_INET)
22921}
22922
22923#[no_mangle]
22924pub unsafe extern "C" fn gethostbyname2(name: *const c_char, af: c_int) -> *mut HostEnt {
22925    if name.is_null() { h_errno = HOST_NOT_FOUND; return core::ptr::null_mut(); }
22926    let mut addr = [0u8; 16];
22927    let addrlen = if af == AF_INET6 { 16 } else { 4 };
22928    if inet_pton(af, name, addr.as_mut_ptr() as *mut c_void) == 1 {
22929        core::ptr::copy_nonoverlapping(addr.as_ptr(), HOST_ADDR.as_mut_ptr(), addrlen);
22930        let n = crate::strlen(name);
22931        let l = core::cmp::min(n, 255);
22932        core::ptr::copy_nonoverlapping(name, HOST_NAME.as_mut_ptr(), l);
22933        HOST_NAME[l] = 0;
22934        HOST_ENT.h_name = HOST_NAME.as_mut_ptr();
22935        HOST_ENT.h_aliases = HOST_ALIASES.as_mut_ptr();
22936        HOST_ENT.h_addrtype = af;
22937        HOST_ENT.h_length = addrlen as c_int;
22938        HOST_ADDR_PTRS[0] = HOST_ADDR.as_mut_ptr() as *mut c_char;
22939        HOST_ADDR_PTRS[1] = core::ptr::null_mut();
22940        HOST_ENT.h_addr_list = HOST_ADDR_PTRS.as_mut_ptr();
22941        h_errno = 0;
22942        return &raw mut HOST_ENT;
22943    }
22944    let mut answer = [0u8; 512];
22945    let qtype = if af == AF_INET6 { 28 } else { 1 };
22946    let r = res_query(name, 1, qtype, answer.as_mut_ptr(), answer.len() as c_int);
22947    if r < 0 {
22948        if h_errno == 0 { h_errno = TRY_AGAIN; }
22949        return core::ptr::null_mut();
22950    }
22951    let ent = host_from_answer(answer.as_ptr(), r, af);
22952    if !ent.is_null() {
22953        let n = crate::strlen(name);
22954        let l = core::cmp::min(n, 255);
22955        core::ptr::copy_nonoverlapping(name, HOST_NAME.as_mut_ptr(), l);
22956        HOST_NAME[l] = 0;
22957    } else {
22958        h_errno = HOST_NOT_FOUND;
22959    }
22960    ent
22961}
22962
22963#[no_mangle]
22964pub unsafe extern "C" fn gethostbyaddr(addr: *const c_void, len: u32, type_: c_int) -> *mut HostEnt {
22965    if addr.is_null() || type_ != AF_INET || len != 4 { h_errno = HOST_NOT_FOUND; return core::ptr::null_mut(); }
22966    let a = addr as *const u8;
22967    let mut name = [0u8; 128];
22968    let mut pos = 0;
22969    for i in 0..4 {
22970        let v = *a.add(3 - i) as u32;
22971        if v >= 100 { name[pos] = b'0' + (v / 100) as u8; pos += 1; }
22972        if v >= 10 { name[pos] = b'0' + ((v / 10) % 10) as u8; pos += 1; }
22973        name[pos] = b'0' + (v % 10) as u8; pos += 1;
22974        name[pos] = b'.'; pos += 1;
22975    }
22976    let suffix = b"in-addr.arpa";
22977    for &c in suffix { name[pos] = c; pos += 1; }
22978    name[pos] = 0;
22979    let mut answer = [0u8; 512];
22980    let r = res_query(name.as_ptr() as *const c_char, 1, 12, answer.as_mut_ptr(), answer.len() as c_int);
22981    if r < 12 { h_errno = HOST_NOT_FOUND; return core::ptr::null_mut(); }
22982    let end = answer.as_ptr().add(r as usize);
22983    let qd = dns_read_u16(answer.as_ptr().add(4)) as usize;
22984    let an = dns_read_u16(answer.as_ptr().add(6)) as usize;
22985    let mut p = answer.as_ptr().add(12);
22986    for _ in 0..qd { p = dns_skip_name(p, end); p = p.add(4); }
22987    for _ in 0..an {
22988        p = dns_skip_name(p, end);
22989        if p.add(10) > end { break; }
22990        let typ = dns_read_u16(p);
22991        let rdlen = dns_read_u16(p.add(8)) as usize;
22992        let rdata = p.add(10);
22993        if typ == 12 && dns_expand_name(rdata, answer.as_ptr(), HOST_NAME.as_mut_ptr(), HOST_NAME.len()) {
22994            let mut o = crate::strlen(HOST_NAME.as_ptr());
22995            if o > 0 && HOST_NAME[o - 1] == b'.' as c_char { o -= 1; }
22996            HOST_NAME[o] = 0;
22997            core::ptr::copy_nonoverlapping(a, HOST_ADDR.as_mut_ptr(), 4);
22998            HOST_ENT.h_name = HOST_NAME.as_mut_ptr();
22999            HOST_ENT.h_aliases = HOST_ALIASES.as_mut_ptr();
23000            HOST_ENT.h_addrtype = AF_INET;
23001            HOST_ENT.h_length = 4;
23002            HOST_ADDR_PTRS[0] = HOST_ADDR.as_mut_ptr() as *mut c_char;
23003            HOST_ADDR_PTRS[1] = core::ptr::null_mut();
23004            HOST_ENT.h_addr_list = HOST_ADDR_PTRS.as_mut_ptr();
23005            h_errno = 0;
23006            return &raw mut HOST_ENT;
23007        }
23008        p = rdata.add(rdlen);
23009    }
23010    h_errno = HOST_NOT_FOUND;
23011    core::ptr::null_mut()
23012}
23013
23014// ============================================================
23015// Legacy syscall/stdio/signal compatibility
23016// ============================================================
23017
23018#[inline]
23019fn sysno(x86_64: i64, generic: i64) -> i64 {
23020    if cfg!(target_arch = "x86_64") { x86_64 } else { generic }
23021}
23022
23023#[no_mangle]
23024pub unsafe extern "C" fn settimeofday(tv: *const c_void, tz: *const c_void) -> c_int {
23025    let r = <Arch as Syscalls>::syscall2(sysno(164, 170), tv as i64, tz as i64);
23026    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
23027}
23028
23029#[no_mangle]
23030pub unsafe extern "C" fn stime(t: *const i64) -> c_int {
23031    if t.is_null() { ERRNO!() = EINVAL; return -1; }
23032    let tv = timeval { tv_sec: *t, tv_usec: 0 };
23033    settimeofday(&tv as *const timeval as *const c_void, core::ptr::null())
23034}
23035
23036#[no_mangle]
23037pub unsafe extern "C" fn times(buf: *mut c_void) -> c_long {
23038    let r = <Arch as Syscalls>::syscall1(sysno(100, 153), buf as i64);
23039    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { r as c_long }
23040}
23041
23042#[no_mangle]
23043pub unsafe extern "C" fn splice(fd_in: c_int, off_in: *mut i64, fd_out: c_int, off_out: *mut i64, len: usize, flags: u32) -> isize {
23044    let r = <Arch as Syscalls>::syscall6(sysno(275, 76), fd_in as i64, off_in as i64, fd_out as i64, off_out as i64, len as i64, flags as i64);
23045    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { r as isize }
23046}
23047
23048#[no_mangle]
23049pub unsafe extern "C" fn tee(fd_in: c_int, fd_out: c_int, len: usize, flags: u32) -> isize {
23050    let r = <Arch as Syscalls>::syscall4(sysno(276, 77), fd_in as i64, fd_out as i64, len as i64, flags as i64);
23051    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { r as isize }
23052}
23053
23054#[no_mangle]
23055pub unsafe extern "C" fn statx(dirfd: c_int, path: *const c_char, flags: c_int, mask: u32, buf: *mut c_void) -> c_int {
23056    let r = <Arch as Syscalls>::syscall5(sysno(332, 291), dirfd as i64, path as i64, flags as i64, mask as i64, buf as i64);
23057    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
23058}
23059
23060#[no_mangle]
23061pub unsafe extern "C" fn setns(fd: c_int, nstype: c_int) -> c_int {
23062    let r = <Arch as Syscalls>::syscall2(SYS_SETNS, fd as i64, nstype as i64);
23063    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
23064}
23065
23066#[no_mangle]
23067pub unsafe extern "C" fn setfsuid(uid: u32) -> c_int {
23068    <Arch as Syscalls>::syscall1(sysno(122, 151), uid as i64) as c_int
23069}
23070
23071#[no_mangle]
23072pub unsafe extern "C" fn setfsgid(gid: u32) -> c_int {
23073    <Arch as Syscalls>::syscall1(sysno(123, 152), gid as i64) as c_int
23074}
23075
23076#[no_mangle]
23077pub unsafe extern "C" fn setregid(rgid: u32, egid: u32) -> c_int {
23078    let r = <Arch as Syscalls>::syscall2(sysno(114, 143), rgid as i64, egid as i64);
23079    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
23080}
23081
23082#[no_mangle]
23083pub unsafe extern "C" fn process_vm_readv(pid: c_int, local: *const c_void, liovcnt: usize, remote: *const c_void, riovcnt: usize, flags: u64) -> isize {
23084    let r = <Arch as Syscalls>::syscall6(sysno(310, 270), pid as i64, local as i64, liovcnt as i64, remote as i64, riovcnt as i64, flags as i64);
23085    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { r as isize }
23086}
23087
23088#[no_mangle]
23089pub unsafe extern "C" fn process_vm_writev(pid: c_int, local: *const c_void, liovcnt: usize, remote: *const c_void, riovcnt: usize, flags: u64) -> isize {
23090    let r = <Arch as Syscalls>::syscall6(sysno(311, 271), pid as i64, local as i64, liovcnt as i64, remote as i64, riovcnt as i64, flags as i64);
23091    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { r as isize }
23092}
23093
23094#[no_mangle]
23095pub unsafe extern "C" fn tcgetwinsize(fd: c_int, ws: *mut c_void) -> c_int {
23096    if fd < 0 || ws.is_null() { ERRNO!() = EINVAL; return -1; }
23097    let r = <Arch as Syscalls>::syscall3(SYS_IOCTL, fd as i64, 0x5413, ws as i64);
23098    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
23099}
23100
23101#[no_mangle]
23102pub unsafe extern "C" fn tcsetwinsize(fd: c_int, ws: *const c_void) -> c_int {
23103    if fd < 0 || ws.is_null() { ERRNO!() = EINVAL; return -1; }
23104    let r = <Arch as Syscalls>::syscall3(SYS_IOCTL, fd as i64, 0x5414, ws as i64);
23105    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
23106}
23107
23108#[no_mangle]
23109pub unsafe extern "C" fn sockatmark(fd: c_int) -> c_int {
23110    let mut at: c_int = 0;
23111    let r = <Arch as Syscalls>::syscall3(SYS_IOCTL, fd as i64, 0x8905, &mut at as *mut c_int as i64);
23112    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { at }
23113}
23114
23115#[no_mangle]
23116pub unsafe extern "C" fn isastream(fd: c_int) -> c_int {
23117    let mut buf = [0u8; 64];
23118    let r = <Arch as Syscalls>::syscall3(SYS_IOCTL, fd as i64, 0x5401, buf.as_mut_ptr() as i64);
23119    if r < 0 { ERRNO!() = (-r) as c_int; 0 } else { 1 }
23120}
23121
23122#[no_mangle]
23123pub unsafe extern "C" fn posix_madvise(addr: *mut c_void, len: usize, advice: c_int) -> c_int {
23124    madvise(addr as *mut u8, len, advice)
23125}
23126
23127#[no_mangle]
23128pub unsafe extern "C" fn secure_getenv(name: *const c_char) -> *mut c_char {
23129    if getauxval(23) != 0 { return core::ptr::null_mut(); }
23130    getenv(name)
23131}
23132
23133#[no_mangle]
23134pub unsafe extern "C" fn sigandset(dest: *mut c_ulong, left: *const c_ulong, right: *const c_ulong) -> c_int {
23135    if dest.is_null() || left.is_null() || right.is_null() { return -1; }
23136    *dest = *left & *right;
23137    0
23138}
23139
23140#[no_mangle]
23141pub unsafe extern "C" fn sigorset(dest: *mut c_ulong, left: *const c_ulong, right: *const c_ulong) -> c_int {
23142    if dest.is_null() || left.is_null() || right.is_null() { return -1; }
23143    *dest = *left | *right;
23144    0
23145}
23146
23147#[no_mangle]
23148pub unsafe extern "C" fn sigisemptyset(set: *const c_ulong) -> c_int {
23149    if set.is_null() { return 1; }
23150    (*set == 0) as c_int
23151}
23152
23153#[no_mangle]
23154pub unsafe extern "C" fn sighold(sig: c_int) -> c_int {
23155    let set: c_ulong = 1u64 << (sig - 1) as u32;
23156    sigprocmask(0, &set, core::ptr::null_mut());
23157    0
23158}
23159
23160#[no_mangle]
23161pub unsafe extern "C" fn sigignore(sig: c_int) -> c_int {
23162    let _ = signal(sig, 1);
23163    0
23164}
23165
23166#[no_mangle]
23167pub unsafe extern "C" fn sigrelse(sig: c_int) -> c_int {
23168    let set: c_ulong = 1u64 << (sig - 1) as u32;
23169    sigprocmask(1, &set, core::ptr::null_mut());
23170    0
23171}
23172
23173#[no_mangle]
23174pub unsafe extern "C" fn siginterrupt(sig: c_int, flag: c_int) -> c_int {
23175    let mut sa: sigaction = core::mem::zeroed();
23176    if sigaction(sig, core::ptr::null(), &mut sa) != 0 { return -1; }
23177    if flag != 0 { sa.sa_flags &= !(SA_RESTART as c_int); } else { sa.sa_flags |= SA_RESTART as c_int; }
23178    sigaction(sig, &sa, core::ptr::null_mut())
23179}
23180
23181#[no_mangle]
23182pub unsafe extern "C" fn sigpause(sig: c_int) -> c_int {
23183    let set: c_ulong = 1u64 << (sig - 1) as u32;
23184    sigsuspend(&set)
23185}
23186
23187#[no_mangle]
23188pub unsafe extern "C" fn semtimedop(semid: c_int, sops: *mut c_void, nsops: usize, timeout: *const timespec) -> c_int {
23189    let r = <Arch as Syscalls>::syscall4(sysno(220, 192), semid as i64, sops as i64, nsops as i64, timeout as i64);
23190    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { r as c_int }
23191}
23192
23193#[no_mangle]
23194pub unsafe extern "C" fn wait3(status: *mut c_int, options: c_int, rusage: *mut c_void) -> i32 {
23195    wait4(-1, status, options, rusage) as i32
23196}
23197
23198#[no_mangle]
23199pub unsafe extern "C" fn tempnam(dir: *const c_char, pfx: *const c_char) -> *mut c_char {
23200    let mut path = [0 as c_char; 256];
23201    let mut i = 0;
23202    if !dir.is_null() {
23203        while *dir.add(i) != 0 && i < 200 { path[i] = *dir.add(i); i += 1; }
23204        if i > 0 && path[i-1] != b'/' as c_char { path[i] = b'/' as c_char; i += 1; }
23205    } else {
23206        for &c in b"/tmp/" { path[i] = c as c_char; i += 1; }
23207    }
23208    if !pfx.is_null() {
23209        let mut j = 0;
23210        while *pfx.add(j) != 0 && i < 220 { path[i] = *pfx.add(j); i += 1; j += 1; }
23211    }
23212    for &c in b"XXXXXX" { path[i] = c as c_char; i += 1; }
23213    path[i] = 0;
23214    let fd = mkstemp(path.as_mut_ptr());
23215    if fd < 0 { return core::ptr::null_mut(); }
23216    close(fd);
23217    unlink(path.as_ptr());
23218    let n = strlen(path.as_ptr());
23219    let out = malloc(n + 1) as *mut c_char;
23220    if out.is_null() { return core::ptr::null_mut(); }
23221    core::ptr::copy_nonoverlapping(path.as_ptr(), out, n + 1);
23222    out
23223}
23224
23225#[no_mangle]
23226pub unsafe extern "C" fn putwc(c: u32, f: *mut c_void) -> u32 {
23227    fputwc(c as c_int, f as *mut FILE) as u32
23228}
23229
23230#[no_mangle]
23231pub unsafe extern "C" fn __uflow(f: *mut c_void) -> c_int {
23232    fgetc(f as *mut FILE)
23233}
23234
23235#[no_mangle]
23236pub unsafe extern "C" fn __xmknod(_ver: c_int, path: *const c_char, mode: c_uint, dev: u64) -> c_int {
23237    mknod(path, mode, dev)
23238}
23239
23240#[no_mangle]
23241pub unsafe extern "C" fn __xmknodat(_ver: c_int, dirfd: c_int, path: *const c_char, mode: c_uint, dev: u64) -> c_int {
23242    mknodat(dirfd, path, mode, dev)
23243}
23244
23245#[no_mangle]
23246pub unsafe extern "C" fn name_to_handle_at(dirfd: c_int, path: *const c_char, handle: *mut c_void, mount_id: *mut c_int, flags: c_int) -> c_int {
23247    let r = <Arch as Syscalls>::syscall5(sysno(303, 264), dirfd as i64, path as i64, handle as i64, mount_id as i64, flags as i64);
23248    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { 0 }
23249}
23250
23251#[no_mangle]
23252pub unsafe extern "C" fn open_by_handle_at(mount_fd: c_int, handle: *mut c_void, flags: c_int) -> c_int {
23253    let r = <Arch as Syscalls>::syscall3(sysno(304, 265), mount_fd as i64, handle as i64, flags as i64);
23254    if r < 0 { ERRNO!() = (-r) as c_int; -1 } else { r as c_int }
23255}
23256
23257#[no_mangle]
23258pub unsafe extern "C" fn dn_skipname(comp: *const u8, eom: *const u8) -> c_int {
23259    let p = dns_skip_name(comp, eom);
23260    if p <= eom { (p as isize - comp as isize) as c_int } else { -1 }
23261}
23262
23263#[no_mangle]
23264pub unsafe extern "C" fn dn_comp(exp: *const c_char, dst: *mut u8, dstsiz: c_int, _dnptrs: *mut *mut u8, _lastdnptr: *mut *mut u8) -> c_int {
23265    if exp.is_null() || dst.is_null() || dstsiz <= 0 { return -1; }
23266    let mut o = 0usize;
23267    let mut p = exp;
23268    loop {
23269        let start = p;
23270        while *p != 0 && *p != b'.' as c_char { p = p.add(1); }
23271        let l = p as usize - start as usize;
23272        if l > 63 || o + l + 2 > dstsiz as usize { return -1; }
23273        *dst.add(o) = l as u8; o += 1;
23274        core::ptr::copy_nonoverlapping(start as *const u8, dst.add(o), l);
23275        o += l;
23276        if *p == 0 { break; }
23277        p = p.add(1);
23278    }
23279    if o + 1 > dstsiz as usize { return -1; }
23280    *dst.add(o) = 0;
23281    (o + 1) as c_int
23282}
23283
23284// ============================================================
23285// Shadow database
23286// ============================================================
23287
23288#[repr(C)]
23289#[derive(Copy, Clone)]
23290pub struct Spwd {
23291    pub sp_namp: *mut c_char,
23292    pub sp_pwdp: *mut c_char,
23293    pub sp_lstchg: c_long,
23294    pub sp_min: c_long,
23295    pub sp_max: c_long,
23296    pub sp_warn: c_long,
23297    pub sp_inact: c_long,
23298    pub sp_expire: c_long,
23299    pub sp_flag: c_ulong,
23300}
23301
23302static mut SPWD_ENT: Spwd = Spwd {
23303    sp_namp: core::ptr::null_mut(), sp_pwdp: core::ptr::null_mut(),
23304    sp_lstchg: 0, sp_min: 0, sp_max: 0, sp_warn: 0, sp_inact: 0,
23305    sp_expire: 0, sp_flag: 0,
23306};
23307static mut SPWD_BUF: [c_char; 1024] = [0; 1024];
23308static mut SPWD_FILE: *mut FILE = core::ptr::null_mut();
23309
23310unsafe fn parse_spwd(line: *const c_char, ent: *mut Spwd, buf: *mut c_char, buflen: usize) -> bool {
23311    let mut src = line;
23312    let mut o = 0usize;
23313    let mut fields: [*mut c_char; 9] = [core::ptr::null_mut(); 9];
23314    let mut n = 0usize;
23315    while n < 9 {
23316        let start = o;
23317        while *src != 0 && *src != b':' as c_char {
23318            if o + 1 >= buflen { return false; }
23319            *buf.add(o) = *src;
23320            o += 1;
23321            src = src.add(1);
23322        }
23323        *buf.add(o) = 0;
23324        o += 1;
23325        fields[n] = buf.add(start);
23326        n += 1;
23327        if *src == 0 { break; }
23328        src = src.add(1);
23329    }
23330    if n < 9 { return false; }
23331    (*ent).sp_namp = fields[0];
23332    (*ent).sp_pwdp = fields[1];
23333    (*ent).sp_lstchg = crate::strtol(fields[2], core::ptr::null_mut(), 10) as c_long;
23334    (*ent).sp_min = crate::strtol(fields[3], core::ptr::null_mut(), 10) as c_long;
23335    (*ent).sp_max = crate::strtol(fields[4], core::ptr::null_mut(), 10) as c_long;
23336    (*ent).sp_warn = crate::strtol(fields[5], core::ptr::null_mut(), 10) as c_long;
23337    (*ent).sp_inact = crate::strtol(fields[6], core::ptr::null_mut(), 10) as c_long;
23338    (*ent).sp_expire = crate::strtol(fields[7], core::ptr::null_mut(), 10) as c_long;
23339    (*ent).sp_flag = crate::strtoul(fields[8], core::ptr::null_mut(), 10) as c_ulong;
23340    true
23341}
23342
23343#[no_mangle]
23344pub unsafe extern "C" fn setspent() {
23345    endspent();
23346    SPWD_FILE = crate::fopen(b"/etc/shadow\0".as_ptr() as *const c_char, b"r\0".as_ptr() as *const c_char);
23347}
23348
23349#[no_mangle]
23350pub unsafe extern "C" fn endspent() {
23351    if !SPWD_FILE.is_null() { crate::fclose(SPWD_FILE); SPWD_FILE = core::ptr::null_mut(); }
23352}
23353
23354#[no_mangle]
23355pub unsafe extern "C" fn getspent() -> *mut Spwd {
23356    if SPWD_FILE.is_null() { setspent(); if SPWD_FILE.is_null() { return core::ptr::null_mut(); } }
23357    let mut line = [0 as c_char; 1024];
23358    loop {
23359        if crate::fgets(line.as_mut_ptr(), 1024, SPWD_FILE).is_null() { return core::ptr::null_mut(); }
23360        if parse_spwd(line.as_ptr(), &raw mut SPWD_ENT, SPWD_BUF.as_mut_ptr(), SPWD_BUF.len()) {
23361            return &raw mut SPWD_ENT;
23362        }
23363    }
23364}
23365
23366#[no_mangle]
23367pub unsafe extern "C" fn fgetspent(f: *mut FILE) -> *mut Spwd {
23368    if f.is_null() { return core::ptr::null_mut(); }
23369    let mut line = [0 as c_char; 1024];
23370    loop {
23371        if crate::fgets(line.as_mut_ptr(), 1024, f).is_null() { return core::ptr::null_mut(); }
23372        if parse_spwd(line.as_ptr(), &raw mut SPWD_ENT, SPWD_BUF.as_mut_ptr(), SPWD_BUF.len()) {
23373            return &raw mut SPWD_ENT;
23374        }
23375    }
23376}
23377
23378#[no_mangle]
23379pub unsafe extern "C" fn getspnam(name: *const c_char) -> *mut Spwd {
23380    if name.is_null() { return core::ptr::null_mut(); }
23381    setspent();
23382    loop {
23383        let sp = getspent();
23384        if sp.is_null() { break; }
23385        if crate::strcmp((*sp).sp_namp as *const u8, name as *const u8) == 0 { endspent(); return sp; }
23386    }
23387    endspent();
23388    core::ptr::null_mut()
23389}
23390
23391#[no_mangle]
23392pub unsafe extern "C" fn getspnam_r(name: *const c_char, sp: *mut Spwd, _buf: *mut c_char, _buflen: usize, result: *mut *mut Spwd) -> c_int {
23393    if result.is_null() { return EINVAL; }
23394    let p = getspnam(name);
23395    if p.is_null() { *result = core::ptr::null_mut(); 0 } else { *sp = *p; *result = sp; 0 }
23396}
23397
23398#[no_mangle]
23399pub unsafe extern "C" fn putspent(sp: *const Spwd, f: *mut FILE) -> c_int {
23400    if sp.is_null() || f.is_null() { return -1; }
23401    crate::fprintf(f, b"%s:%s:%ld:%ld:%ld:%ld:%ld:%ld:%lu\\n\0".as_ptr() as *const c_char,
23402        (*sp).sp_namp, (*sp).sp_pwdp, (*sp).sp_lstchg, (*sp).sp_min, (*sp).sp_max,
23403        (*sp).sp_warn, (*sp).sp_inact, (*sp).sp_expire, (*sp).sp_flag)
23404}
23405
23406#[no_mangle]
23407pub unsafe extern "C" fn getgrouplist(user: *const c_char, group: u32, groups: *mut u32, ngroups: *mut i32) -> c_int {
23408    if user.is_null() || groups.is_null() || ngroups.is_null() { return -1; }
23409    let max = *ngroups as usize;
23410    let mut n = 0usize;
23411    if n < max { *groups.add(n) = group; }
23412    n += 1;
23413    setgrent();
23414    loop {
23415        let g = getgrent();
23416        if g.is_null() { break; }
23417        if (*g).gr_gid == group { continue; }
23418        let mut found = false;
23419        let mut i = 0;
23420        while !(*(*g).gr_mem.add(i)).is_null() {
23421            if crate::strcmp(*(*g).gr_mem.add(i) as *const u8, user as *const u8) == 0 {
23422                found = true;
23423                break;
23424            }
23425            i += 1;
23426        }
23427        if found {
23428            if n < max { *groups.add(n) = (*g).gr_gid; }
23429            n += 1;
23430        }
23431    }
23432    endgrent();
23433    if n > max { *ngroups = n as i32; return -1; }
23434    *ngroups = n as i32;
23435    n as c_int
23436}