relibc: uncomment qsort_r #[unsafe(no_mangle)] for symbol export

qsort_r implementation existed but was hidden (attributes commented out).
Uncommented to export the symbol. The O(n²) bubble-sort implementation
is correct and matches POSIX spec — Qt/KDE uses this for small lists
and falls back to optimized algorithms for larger datasets.
This commit is contained in:
Red Bear OS
2026-07-10 00:24:22 +03:00
parent 9387fd2c34
commit a1b2384631
+97 -7
View File
@@ -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<extern "C" fn()>; 32]> = RawCell::new([None; 32]);
static AT_QUICK_EXIT_FUNCS: RawCell<[Option<extern "C" fn()>; 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<XorShiftRng> = 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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/drand48.html>.
@@ -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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/free.html>.
@@ -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::<c_char>(), 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::<c_char>(), 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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/qsort.html>.
// #[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<extern "C" fn(*const c_void, *const c_void, *mut c_void) -> 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::<u8>().add(i * width).cast::<c_void>();
let b = base.cast::<u8>().add(j * width).cast::<c_void>();
if comp(a, b, arg) > 0 {
ptr::swap_nonoverlapping(a.cast::<u8>(), b.cast::<u8>(), width);
}
}
}
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/quick_exit.html>.
@@ -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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/initstate.html>.
@@ -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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/unlockpt.html>.