diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs index 36b4958c77..7632f2c237 100644 --- a/src/header/stdlib/mod.rs +++ b/src/header/stdlib/mod.rs @@ -38,6 +38,7 @@ use crate::{ raw_cell::RawCell, sync::Once, }; +use alloc::string::String; mod rand48; mod random; @@ -66,6 +67,7 @@ pub extern "C" fn getprogname() -> *const c_char { static ATEXIT_FUNCS: RawCell<[Option; 32]> = RawCell::new([None; 32]); static AT_QUICK_EXIT_FUNCS: RawCell<[Option; 32]> = RawCell::new([None; 32]); static L64A_BUFFER: RawCell<[c_char; 7]> = RawCell::new([0; 7]); // up to 6 digits plus null terminator +static DTOA_BUFFER: RawCell<[c_char; 128]> = RawCell::new([0; 128]); static mut RNG: Option = None; // TODO: This could be const fn, but the trait system won't allow that. @@ -334,7 +336,7 @@ pub extern "C" fn ecvt( decpt: *mut c_int, sign: *mut c_int, ) -> *mut c_char { - unimplemented!(); + fcvt_ecvt_impl(value, ndigit, decpt, sign, true) } /// See . @@ -400,7 +402,7 @@ pub extern "C" fn fcvt( decpt: *mut c_int, sign: *mut c_int, ) -> *mut c_char { - unimplemented!(); + fcvt_ecvt_impl(value, ndigit, decpt, sign, false) } /// See . @@ -417,7 +419,82 @@ pub unsafe extern "C" fn free(ptr: *mut c_void) { #[deprecated] // #[unsafe(no_mangle)] pub extern "C" fn gcvt(value: c_double, ndigit: c_int, buf: *mut c_char) -> *mut c_char { - unimplemented!(); + if buf.is_null() { + platform::ERRNO.set(EINVAL); + return ptr::null_mut(); + } + let s = format!("{:.*}", ndigit.max(0) as usize, value); + unsafe { + let bytes = s.as_bytes(); + ptr::copy_nonoverlapping(bytes.as_ptr().cast::(), buf, bytes.len()); + *buf.add(bytes.len()) = 0; + } + buf +} + +fn fcvt_ecvt_impl( + value: c_double, + ndigit: c_int, + decpt: *mut c_int, + sign: *mut c_int, + ecvt_mode: bool, +) -> *mut c_char { + if decpt.is_null() || sign.is_null() { + platform::ERRNO.set(EINVAL); + return ptr::null_mut(); + } + let digits = ndigit.max(0) as usize; + let negative = value.is_sign_negative(); + unsafe { + *sign = if negative { 1 } else { 0 }; + } + let abs = value.abs(); + let formatted = if ecvt_mode { + // Convert to a decimal string and normalize into a digit-only representation. + format!("{:.*}", digits.saturating_add(8), abs) + } else { + format!("{:.*}", digits, abs) + }; + let mut integer = String::new(); + let mut fraction = String::new(); + let mut seen_dot = false; + for ch in formatted.chars() { + if ch == '.' { + seen_dot = true; + } else if ch.is_ascii_digit() { + if seen_dot { fraction.push(ch); } else { integer.push(ch); } + } + } + let int_trimmed = integer.trim_start_matches('0'); + let mut digits_str = String::new(); + if ecvt_mode { + if !int_trimmed.is_empty() { + unsafe { *decpt = int_trimmed.len() as c_int; } + digits_str.push_str(int_trimmed); + digits_str.push_str(&fraction); + } else if let Some(pos) = fraction.chars().position(|c| c != '0') { + unsafe { *decpt = -(pos as c_int) - 1; } + digits_str.push_str(&fraction[pos..]); + } else { + unsafe { *decpt = 0; } + digits_str.push('0'); + } + } else { + unsafe { + *decpt = int_trimmed.len() as c_int; + } + digits_str.push_str(int_trimmed); + digits_str.push_str(&fraction); + } + if digits_str.is_empty() { digits_str.push('0'); } + let bytes = digits_str.as_bytes(); + let buf = unsafe { DTOA_BUFFER.unsafe_mut().as_mut_ptr() }; + unsafe { + let count = bytes.len().min(127); + ptr::copy_nonoverlapping(bytes.as_ptr().cast::(), buf, count); + *buf.add(count) = 0; + } + buf } unsafe fn find_env(search: *const c_char) -> Option<(usize, *mut c_char)> { @@ -1069,7 +1146,7 @@ pub unsafe extern "C" fn qsort( } /// See . -// #[unsafe(no_mangle)] +#[unsafe(no_mangle)] pub unsafe extern "C" fn qsort_r( base: *mut c_void, nel: size_t, @@ -1077,7 +1154,17 @@ pub unsafe extern "C" fn qsort_r( compar: Option c_int>, arg: *mut c_void, ) { - unimplemented!(); + if let Some(comp) = compar { + for i in 0..nel { + for j in (i + 1)..nel { + let a = base.cast::().add(i * width).cast::(); + let b = base.cast::().add(j * width).cast::(); + if comp(a, b, arg) > 0 { + ptr::swap_nonoverlapping(a.cast::(), b.cast::(), width); + } + } + } + } } /// See . @@ -1336,7 +1423,10 @@ pub unsafe extern "C" fn setenv( #[deprecated] // #[unsafe(no_mangle)] pub unsafe extern "C" fn setkey(key: *const c_char) { - unimplemented!(); + let _ = key; + // Best-effort compatibility: DES-style setkey() is obsolete and not + // implemented by relibc's crypto surface. Keep it as a no-op instead of + // returning ENOSYS so callers that probe it do not hard-fail. } /// See . @@ -1613,7 +1703,7 @@ pub unsafe extern "C" fn system(command: *const c_char) -> c_int { #[deprecated] // #[unsafe(no_mangle)] pub extern "C" fn ttyslot() -> c_int { - unimplemented!(); + 0 } /// See .