//! stdlib implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/stdlib.h.html use core::{intrinsics, iter, mem, ptr, slice}; use rand::distributions::Alphanumeric; use rand::prng::XorShiftRng; use rand::rngs::JitterRng; use rand::{Rng, SeedableRng}; use c_str::CStr; use fs::File; use header::errno::*; use header::fcntl::*; use header::limits; use header::string::*; use header::time::constants::CLOCK_MONOTONIC; use header::time::timespec; use header::wchar::*; use header::{ctype, errno, unistd}; use platform; use platform::types::*; use platform::{Pal, Sys}; mod sort; pub const EXIT_FAILURE: c_int = 1; pub const EXIT_SUCCESS: c_int = 0; pub const RAND_MAX: c_int = 2_147_483_647; //Maximum number of bytes in a multibyte character for the current locale pub const MB_CUR_MAX: c_int = 4; //Maximum number of bytes in a multibyte characters for any locale pub const MB_LEN_MAX: c_int = 4; static mut ATEXIT_FUNCS: [Option; 32] = [None; 32]; static mut RNG: Option = None; #[no_mangle] pub extern "C" fn _Exit(status: c_int) { unistd::_exit(status); } #[no_mangle] pub unsafe extern "C" fn a64l(s: *const c_char) -> c_long { if s.is_null() { return 0; } let mut l: c_long = 0; // a64l does not support more than 6 characters at once for x in 0..6 { let c = *s.offset(x); if c == 0 { // string is null terminated return l; } // ASCII to base64 conversion: let mut bits: c_long = if c < 58 { (c - 46) as c_long // ./0123456789 } else if c < 91 { (c - 53) as c_long // A-Z } else { (c - 59) as c_long // a-z }; bits <<= 6 * x; l |= bits; } l } #[no_mangle] pub unsafe extern "C" fn abort() { intrinsics::abort(); } #[no_mangle] pub extern "C" fn abs(i: c_int) -> c_int { i.abs() } #[no_mangle] pub unsafe extern "C" fn aligned_alloc(alignment: size_t, size: size_t) -> *mut c_void { if size % alignment != 0 { return ptr::null_mut(); } memalign(alignment, size) } #[no_mangle] pub unsafe extern "C" fn atexit(func: Option) -> c_int { for i in 0..ATEXIT_FUNCS.len() { if ATEXIT_FUNCS[i] == None { ATEXIT_FUNCS[i] = func; return 0; } } 1 } #[no_mangle] pub unsafe extern "C" fn atof(s: *const c_char) -> c_double { strtod(s, ptr::null_mut()) } macro_rules! dec_num_from_ascii { ($s:expr, $t:ty) => { unsafe { let mut s = $s; // Iterate past whitespace while ctype::isspace(*s as c_int) != 0 { s = s.offset(1); } // Find out if there is a - sign let neg_sign = match *s { 0x2d => { s = s.offset(1); true } // '+' increment s and continue parsing 0x2b => { s = s.offset(1); false } _ => false, }; let mut n: $t = 0; while ctype::isdigit(*s as c_int) != 0 { n = 10 * n - (*s as $t - 0x30); s = s.offset(1); } if neg_sign { n } else { -n } } }; } #[no_mangle] pub extern "C" fn atoi(s: *const c_char) -> c_int { dec_num_from_ascii!(s, c_int) } #[no_mangle] pub extern "C" fn atol(s: *const c_char) -> c_long { dec_num_from_ascii!(s, c_long) } #[no_mangle] pub extern "C" fn atoll(s: *const c_char) -> c_longlong { dec_num_from_ascii!(s, c_longlong) } unsafe extern "C" fn void_cmp(a: *const c_void, b: *const c_void) -> c_int { *(a as *const i32) - *(b as *const i32) as c_int } #[no_mangle] pub unsafe extern "C" fn bsearch( key: *const c_void, base: *const c_void, nel: size_t, width: size_t, compar: Option c_int>, ) -> *mut c_void { let mut start = base; let mut len = nel; let cmp_fn = compar.unwrap_or(void_cmp); while len > 0 { let med = (start as size_t + (len >> 1) * width) as *const c_void; let diff = cmp_fn(key, med); if diff == 0 { return med as *mut c_void; } else if diff > 0 { start = (med as usize + width) as *const c_void; len -= 1; } len >>= 1; } ptr::null_mut() } #[no_mangle] pub unsafe extern "C" fn calloc(nelem: size_t, elsize: size_t) -> *mut c_void { //Handle possible integer overflow in size calculation let size_result = nelem.checked_mul(elsize); match size_result { Some(size) => { let ptr = malloc(size); if !ptr.is_null() { intrinsics::write_bytes(ptr as *mut u8, 0, size); } ptr }, None => core::ptr::null_mut() } } #[repr(C)] pub struct div_t { quot: c_int, rem: c_int, } #[no_mangle] pub extern "C" fn div(numer: c_int, denom: c_int) -> div_t { div_t { quot: numer / denom, rem: numer % denom, } } // #[no_mangle] pub extern "C" fn drand48() -> c_double { unimplemented!(); } // #[no_mangle] pub extern "C" fn ecvt( value: c_double, ndigit: c_int, decpt: *mut c_int, sign: *mut c_int, ) -> *mut c_char { unimplemented!(); } // #[no_mangle] pub extern "C" fn erand(xsubi: [c_ushort; 3]) -> c_double { unimplemented!(); } #[no_mangle] pub unsafe extern "C" fn exit(status: c_int) { extern "C" { static __fini_array_start: extern "C" fn(); static __fini_array_end: extern "C" fn(); fn pthread_terminate(); fn _fini(); } for i in (0..ATEXIT_FUNCS.len()).rev() { if let Some(func) = ATEXIT_FUNCS[i] { (func)(); } } // Look for the neighbor functions in memory until the end let mut f = &__fini_array_end as *const _; while f > &__fini_array_start { f = f.offset(-1); (*f)(); } _fini(); pthread_terminate(); Sys::exit(status); } // #[no_mangle] pub extern "C" fn fcvt( value: c_double, ndigit: c_int, decpt: *mut c_int, sign: *mut c_int, ) -> *mut c_char { unimplemented!(); } #[no_mangle] pub unsafe extern "C" fn free(ptr: *mut c_void) { platform::free(ptr); } // #[no_mangle] pub extern "C" fn gcvt(value: c_double, ndigit: c_int, buf: *mut c_char) -> *mut c_char { unimplemented!(); } unsafe fn find_env(search: *const c_char) -> Option<(usize, *mut c_char)> { for (i, item) in platform::inner_environ.iter().enumerate() { let mut item = *item; if item.is_null() { assert_eq!( i, platform::inner_environ.len() - 1, "an early null pointer in environ vector" ); break; } let mut search = search; loop { let end_of_query = *search == 0 || *search == b'=' as c_char; assert_ne!(*item, 0, "environ has an item without value"); if *item == b'=' as c_char || end_of_query { if *item == b'=' as c_char && end_of_query { // Both keys env here return Some((i, item.offset(1))); } else { break; } } if *item != *search { break; } item = item.offset(1); search = search.offset(1); } } None } #[no_mangle] pub unsafe extern "C" fn getenv(name: *const c_char) -> *mut c_char { find_env(name).map(|val| val.1).unwrap_or(ptr::null_mut()) } // #[no_mangle] pub extern "C" fn getsubopt( optionp: *mut *mut c_char, tokens: *const *mut c_char, valuep: *mut *mut c_char, ) -> c_int { unimplemented!(); } // #[no_mangle] pub extern "C" fn grantpt(fildes: c_int) -> c_int { unimplemented!(); } // #[no_mangle] pub extern "C" fn initstate(seec: c_uint, state: *mut c_char, size: size_t) -> *mut c_char { unimplemented!(); } // #[no_mangle] pub extern "C" fn jrand48(xsubi: [c_ushort; 3]) -> c_long { unimplemented!(); } // #[no_mangle] pub extern "C" fn l64a(value: c_long) -> *mut c_char { unimplemented!(); } #[no_mangle] pub extern "C" fn labs(i: c_long) -> c_long { i.abs() } // #[no_mangle] pub extern "C" fn lcong48(param: [c_ushort; 7]) { unimplemented!(); } #[repr(C)] pub struct ldiv_t { quot: c_long, rem: c_long, } #[no_mangle] pub extern "C" fn ldiv(numer: c_long, denom: c_long) -> ldiv_t { ldiv_t { quot: numer / denom, rem: numer % denom, } } #[no_mangle] pub extern "C" fn llabs(i: c_longlong) -> c_longlong { i.abs() } #[repr(C)] pub struct lldiv_t { quot: c_longlong, rem: c_longlong, } #[no_mangle] pub extern "C" fn lldiv(numer: c_longlong, denom: c_longlong) -> lldiv_t { lldiv_t { quot: numer / denom, rem: numer % denom, } } // #[no_mangle] pub extern "C" fn lrand48() -> c_long { unimplemented!(); } #[no_mangle] pub unsafe extern "C" fn malloc(size: size_t) -> *mut c_void { platform::alloc(size) } #[no_mangle] pub unsafe extern "C" fn memalign(alignment: size_t, size: size_t) -> *mut c_void { platform::alloc_align(size, alignment) } #[no_mangle] pub unsafe extern "C" fn mblen(s: *const c_char, n: size_t) -> c_int { let mut wc: wchar_t = 0; let mut state: mbstate_t = mbstate_t {}; let result: usize = mbrtowc(&mut wc, s, n, &mut state); if result == -1isize as usize { return -1; } if result == -2isize as usize { return -1; } result as i32 } #[no_mangle] pub unsafe extern "C" fn mbstowcs(pwcs: *mut wchar_t, mut s: *const c_char, n: size_t) -> size_t { let mut state: mbstate_t = mbstate_t {}; mbsrtowcs(pwcs, &mut s, n, &mut state) } #[no_mangle] pub unsafe extern "C" fn mbtowc(pwc: *mut wchar_t, s: *const c_char, n: size_t) -> c_int { let mut state: mbstate_t = mbstate_t {}; mbrtowc(pwc, s, n, &mut state) as c_int } fn inner_mktemp(name: *mut c_char, suffix_len: c_int, mut attempt: F) -> Option where F: FnMut() -> Option, { let len = unsafe { strlen(name) as c_int }; if len < 6 || suffix_len > len - 6 { unsafe { platform::errno = errno::EINVAL }; return None; } for i in (len - suffix_len - 6)..(len - suffix_len) { if unsafe { *name.offset(i as isize) } != b'X' as c_char { unsafe { platform::errno = errno::EINVAL }; return None; } } let mut rng = JitterRng::new_with_timer(get_nstime); rng.test_timer(); for _ in 0..100 { let char_iter = iter::repeat(()) .map(|()| rng.sample(Alphanumeric)) .take(6) .enumerate(); unsafe { for (i, c) in char_iter { *name.offset((len as isize) - (suffix_len as isize) - (i as isize) - 1) = c as c_char } } if let result @ Some(_) = attempt() { return result; } } unsafe { platform::errno = errno::EEXIST } None } #[no_mangle] pub unsafe extern "C" fn mktemp(name: *mut c_char) -> *mut c_char { if inner_mktemp(name, 0, || { let name = CStr::from_ptr(name); if Sys::access(name, 0) != 0 && platform::errno == ENOENT { Some(()) } else { None } }) .is_none() { *name = 0; } name } fn get_nstime() -> u64 { let mut ts: timespec = unsafe { mem::uninitialized() }; Sys::clock_gettime(CLOCK_MONOTONIC, &mut ts); ts.tv_nsec as u64 } #[no_mangle] pub extern "C" fn mkostemps(name: *mut c_char, suffix_len: c_int, mut flags: c_int) -> c_int { flags &= !O_ACCMODE; flags |= O_RDWR | O_CREAT | O_EXCL; inner_mktemp(name, suffix_len, || { let name = unsafe { CStr::from_ptr(name) }; let fd = Sys::open(name, flags, 0o600); if fd >= 0 { Some(fd) } else { None } }) .unwrap_or(-1) } #[no_mangle] pub extern "C" fn mkstemp(name: *mut c_char) -> c_int { mkostemps(name, 0, 0) } #[no_mangle] pub extern "C" fn mkostemp(name: *mut c_char, flags: c_int) -> c_int { mkostemps(name, 0, flags) } #[no_mangle] pub extern "C" fn mkstemps(name: *mut c_char, suffix_len: c_int) -> c_int { mkostemps(name, suffix_len, 0) } // #[no_mangle] pub extern "C" fn mrand48() -> c_long { unimplemented!(); } // #[no_mangle] pub extern "C" fn nrand48(xsubi: [c_ushort; 3]) -> c_long { unimplemented!(); } // #[no_mangle] pub extern "C" fn ptsname(fildes: c_int) -> *mut c_char { unimplemented!(); } #[no_mangle] pub unsafe extern "C" fn putenv(insert: *mut c_char) -> c_int { assert_ne!(insert, ptr::null_mut(), "putenv(NULL)"); if let Some((i, _)) = find_env(insert) { //TODO: find out why this crashes: platform::free(platform::inner_environ[i] as *mut c_void); platform::inner_environ[i] = insert; } else { let i = platform::inner_environ.len() - 1; assert_eq!( platform::inner_environ[i], ptr::null_mut(), "environ did not end with null" ); platform::inner_environ[i] = insert; platform::inner_environ.push(ptr::null_mut()); platform::environ = platform::inner_environ.as_mut_ptr(); } 0 } #[no_mangle] pub extern "C" fn qsort( base: *mut c_void, nel: size_t, width: size_t, compar: Option c_int>, ) { if let Some(comp) = compar { // XXX: check width too? not specified if nel > 0 { // XXX: maybe try to do mergesort/timsort first and fallback to introsort if memory // allocation fails? not sure what is ideal sort::introsort(base as *mut c_char, nel, width, comp); } } } #[no_mangle] pub unsafe extern "C" fn rand() -> c_int { match RNG { Some(ref mut rng) => rng.gen_range(0, RAND_MAX), None => { let mut rng = XorShiftRng::from_seed([1; 16]); let ret = rng.gen_range(0, RAND_MAX); RNG = Some(rng); ret } } } // #[no_mangle] pub extern "C" fn rand_r(seed: *mut c_uint) -> c_int { unimplemented!(); } // #[no_mangle] pub extern "C" fn random() -> c_long { unimplemented!(); } #[no_mangle] pub unsafe extern "C" fn realloc(ptr: *mut c_void, size: size_t) -> *mut c_void { platform::realloc(ptr, size) } #[no_mangle] pub unsafe extern "C" fn realpath(pathname: *const c_char, resolved: *mut c_char) -> *mut c_char { let ptr = if resolved.is_null() { malloc(limits::PATH_MAX) as *mut c_char } else { resolved }; let out = slice::from_raw_parts_mut(ptr as *mut u8, limits::PATH_MAX); { let file = match File::open(&CStr::from_ptr(pathname), O_PATH | O_CLOEXEC) { Ok(file) => file, Err(_) => return ptr::null_mut(), }; let len = out.len(); let read = Sys::fpath( *file, &mut out[..len - 1], ); if read < 0 { return ptr::null_mut(); } out[read as usize] = 0; } ptr } // #[no_mangle] pub extern "C" fn seed48(seed16v: [c_ushort; 3]) -> c_ushort { unimplemented!(); } #[no_mangle] pub unsafe extern "C" fn setenv( mut key: *const c_char, mut value: *const c_char, overwrite: c_int, ) -> c_int { let mut key_len = 0; while *key.offset(key_len) != 0 { key_len += 1; } let mut value_len = 0; while *value.offset(value_len) != 0 { value_len += 1; } let index = if let Some((i, existing)) = find_env(key) { if overwrite == 0 { return 0; } let mut existing_len = 0; while *existing.offset(existing_len) != 0 { existing_len += 1; } if existing_len >= value_len { // Reuse existing element's allocation for i in 0..=value_len { *existing.offset(i) = *value.offset(i); } return 0; } else { i } } else { let i = platform::inner_environ.len() - 1; assert_eq!( platform::inner_environ[i], ptr::null_mut(), "environ did not end with null" ); platform::inner_environ.push(ptr::null_mut()); platform::environ = platform::inner_environ.as_mut_ptr(); i }; //platform::free(platform::inner_environ[index] as *mut c_void); let mut ptr = platform::alloc(key_len as usize + 1 + value_len as usize) as *mut c_char; platform::inner_environ[index] = ptr; while *key != 0 { *ptr = *key; ptr = ptr.offset(1); key = key.offset(1); } *ptr = b'=' as c_char; ptr = ptr.offset(1); while *value != 0 { *ptr = *value; ptr = ptr.offset(1); value = value.offset(1); } *ptr = 0; 0 } // #[no_mangle] pub extern "C" fn setkey(key: *const c_char) { unimplemented!(); } // #[no_mangle] pub extern "C" fn setstate(state: *const c_char) -> *mut c_char { unimplemented!(); } #[no_mangle] pub unsafe extern "C" fn srand(seed: c_uint) { RNG = Some(XorShiftRng::from_seed([seed as u8; 16])); } // #[no_mangle] pub extern "C" fn srand48(seed: c_long) { unimplemented!(); } // #[no_mangle] pub extern "C" fn srandom(seed: c_uint) { unimplemented!(); } #[no_mangle] pub unsafe extern "C" fn strtod(s: *const c_char, endptr: *mut *mut c_char) -> c_double { strto_float_impl!(c_double, s, endptr) } #[no_mangle] pub unsafe extern "C" fn strtof(s: *const c_char, endptr: *mut *mut c_char) -> c_float { strto_float_impl!(c_float, s, endptr) } pub fn is_positive(ch: c_char) -> Option<(bool, isize)> { match ch { 0 => None, ch if ch == b'+' as c_char => Some((true, 1)), ch if ch == b'-' as c_char => Some((false, 1)), _ => Some((true, 0)), } } pub unsafe fn detect_base(s: *const c_char) -> Option<(c_int, isize)> { let first = *s as u8; match first { 0 => None, b'0' => { let second = *s.offset(1) as u8; if second == b'X' || second == b'x' { Some((16, 2)) } else if second >= b'0' && second <= b'7' { Some((8, 1)) } else { // in this case, the prefix (0) is going to be the number Some((8, 0)) } } _ => Some((10, 0)), } } pub unsafe fn convert_octal(s: *const c_char) -> Option<(c_ulong, isize, bool)> { if *s != 0 && *s == b'0' as c_char { if let Some((val, idx, overflow)) = convert_integer(s.offset(1), 8) { Some((val, idx + 1, overflow)) } else { // in case the prefix is not actually a prefix Some((0, 1, false)) } } else { None } } pub unsafe fn convert_hex(s: *const c_char) -> Option<(c_ulong, isize, bool)> { if (*s != 0 && *s == b'0' as c_char) && (*s.offset(1) != 0 && (*s.offset(1) == b'x' as c_char || *s.offset(1) == b'X' as c_char)) { convert_integer(s.offset(2), 16).map(|(val, idx, overflow)| (val, idx + 2, overflow)) } else { convert_integer(s, 16).map(|(val, idx, overflow)| (val, idx, overflow)) } } pub unsafe fn convert_integer(s: *const c_char, base: c_int) -> Option<(c_ulong, isize, bool)> { // -1 means the character is invalid #[cfg_attr(rustfmt, rustfmt_skip)] const LOOKUP_TABLE: [c_long; 256] = [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ]; let mut num: c_ulong = 0; let mut idx = 0; let mut overflowed = false; loop { // `-1 as usize` is usize::MAX // `-1 as u8 as usize` is u8::MAX // It extends by the sign bit unless we cast it to unsigned first. let val = LOOKUP_TABLE[*s.offset(idx) as u8 as usize]; if val == -1 || val as c_int >= base { break; } else { if let Some(res) = num .checked_mul(base as c_ulong) .and_then(|num| num.checked_add(val as c_ulong)) { num = res; } else { platform::errno = ERANGE; num = c_ulong::max_value(); overflowed = true; } idx += 1; } } if idx > 0 { Some((num, idx, overflowed)) } else { None } } #[no_mangle] pub unsafe extern "C" fn strtoul( s: *const c_char, endptr: *mut *mut c_char, base: c_int, ) -> c_ulong { strto_impl!( c_ulong, false, c_ulong::max_value(), c_ulong::min_value(), s, endptr, base ) } #[no_mangle] pub unsafe extern "C" fn strtol(s: *const c_char, endptr: *mut *mut c_char, base: c_int) -> c_long { strto_impl!( c_long, true, c_long::max_value(), c_long::min_value(), s, endptr, base ) } #[no_mangle] pub unsafe extern "C" fn strtoull( s: *const c_char, endptr: *mut *mut c_char, base: c_int, ) -> c_ulonglong { strtoul(s, endptr, base) } #[no_mangle] pub unsafe extern "C" fn strtoll( s: *const c_char, endptr: *mut *mut c_char, base: c_int, ) -> c_longlong { strtol(s, endptr, base) } #[no_mangle] pub unsafe extern "C" fn system(command: *const c_char) -> c_int { //TODO: share code with popen let child_pid = unistd::fork(); if child_pid == 0 { let command_nonnull = if command.is_null() { "exit 0\0".as_ptr() } else { command as *const u8 }; let shell = "/bin/sh\0".as_ptr(); let args = [ "sh\0".as_ptr(), "-c\0".as_ptr(), command_nonnull, ptr::null(), ]; unistd::execv(shell as *const c_char, args.as_ptr() as *const *mut c_char); exit(127); unreachable!(); } else if child_pid > 0 { let mut wstatus = 0; if Sys::waitpid(child_pid, &mut wstatus, 0) == !0 { return -1; } wstatus } else { -1 } } // #[no_mangle] pub extern "C" fn ttyslot() -> c_int { unimplemented!(); } // #[no_mangle] pub extern "C" fn unlockpt(fildes: c_int) -> c_int { unimplemented!(); } #[no_mangle] pub unsafe extern "C" fn unsetenv(key: *const c_char) -> c_int { if let Some((i, _)) = find_env(key) { // No need to worry about updating the pointer, this does not // reallocate in any way. And the final null is already shifted back. platform::inner_environ.remove(i); } 0 } #[no_mangle] pub unsafe extern "C" fn valloc(size: size_t) -> *mut c_void { memalign(4096, size) } #[no_mangle] pub extern "C" fn wcstombs(s: *mut c_char, pwcs: *mut *const wchar_t, n: size_t) -> size_t { let mut state: mbstate_t = mbstate_t {}; wcsrtombs(s, pwcs, n, &mut state) } #[no_mangle] pub unsafe extern "C" fn wctomb(s: *mut c_char, wc: wchar_t) -> c_int { let mut state: mbstate_t = mbstate_t {}; let result: usize = wcrtomb(s, wc, &mut state); if result == -1isize as usize { return -1; } if result == -2isize as usize { return -1; } result as c_int }