diff --git a/src/stdlib/src/lib.rs b/src/stdlib/src/lib.rs index 55928ae4c9bfc16a71a64bc7f24e5f6ef46026bd..7439fa52f68a6f4c09d635cea03935b6ce938185 100644 --- a/src/stdlib/src/lib.rs +++ b/src/stdlib/src/lib.rs @@ -388,7 +388,7 @@ pub extern "C" fn qsort( width: size_t, compar: Option<extern "C" fn(*const c_void, *const c_void) -> c_int>, ) { - unimplemented!(); + unimplemented!() } #[no_mangle] @@ -466,7 +466,7 @@ pub extern "C" fn srandom(seed: c_uint) { #[no_mangle] pub unsafe extern "C" fn strtod(s: *const c_char, endptr: *mut *mut c_char) -> c_double { - //TODO: endptr + // TODO: endptr use core::str::FromStr; @@ -480,90 +480,6 @@ pub unsafe extern "C" fn strtod(s: *const c_char, endptr: *mut *mut c_char) -> c } } -#[no_mangle] -pub unsafe extern "C" fn strtol( - s: *const c_char, - endptr: *mut *const c_char, - base: c_int, -) -> c_long { - let set_endptr = |idx: isize| { - if !endptr.is_null() { - *endptr = s.offset(idx); - } - }; - - let invalid_input = || { - platform::errno = EINVAL; - set_endptr(0); - }; - - // only valid bases are 2 through 36 - if base != 0 && (base < 2 || base > 36) { - invalid_input(); - return 0; - } - - let mut idx = 0; - - // skip any whitespace at the beginning of the string - while ctype::isspace(*s.offset(idx) as c_int) != 0 { - idx += 1; - } - - // check for +/- - let positive = match is_positive(*s.offset(idx)) { - Some((pos, i)) => { - idx += i; - pos - } - None => { - invalid_input(); - return 0; - } - }; - - // convert the string to a number - let num_str = s.offset(idx); - let res = match base { - 0 => detect_base(num_str).and_then(|(base, i)| convert_integer(num_str.offset(i), base)), - 8 => convert_octal(num_str), - 16 => convert_hex(num_str), - _ => convert_integer(num_str, base), - }; - - // check for error parsing octal/hex prefix - // also check to ensure a number was indeed parsed - let (num, i, _) = match res { - Some(res) => res, - None => { - invalid_input(); - return 0; - } - }; - idx += i; - - // account for the sign - let mut num = num as c_long; - num = if num.is_negative() { - platform::errno = ERANGE; - if positive { - c_long::max_value() - } else { - c_long::min_value() - } - } else { - if positive { - num - } else { - -num - } - }; - - set_endptr(idx); - - num -} - fn is_positive(ch: c_char) -> Option<(bool, isize)> { match ch { 0 => None, @@ -669,11 +585,125 @@ fn convert_integer(s: *const c_char, base: c_int) -> Option<(c_ulong, isize, boo } } -#[no_mangle] -pub extern "C" fn strtoul(s: *const c_char, endptr: *mut *mut c_char, base: c_int) -> c_ulong { - unimplemented!(); +macro_rules! strto_impl { + ($funcname:ident, $rettype:ty, $signed:expr, $maxval:expr, $minval:expr) => { + #[no_mangle] + pub unsafe extern "C" fn $funcname(s: *const c_char, + endptr: *mut *const c_char, + base: c_int) + -> $rettype { + // ensure these are constants + const CHECK_SIGN: bool = $signed; + const MAX_VAL: $rettype = $maxval; + const MIN_VAL: $rettype = $minval; + + let set_endptr = |idx: isize| { + if !endptr.is_null() { + *endptr = s.offset(idx); + } + }; + + let invalid_input = || { + platform::errno = EINVAL; + set_endptr(0); + }; + + // only valid bases are 2 through 36 + if base != 0 && (base < 2 || base > 36) { + invalid_input(); + return 0; + } + + let mut idx = 0; + + // skip any whitespace at the beginning of the string + while ctype::isspace(*s.offset(idx) as c_int) != 0 { + idx += 1; + } + + // check for +/- + let positive = match is_positive(*s.offset(idx)) { + Some((pos, i)) => { + idx += i; + pos + } + None => { + invalid_input(); + return 0; + } + }; + + // convert the string to a number + let num_str = s.offset(idx); + let res = match base { + 0 => detect_base(num_str).and_then(|(base, i)| { + convert_integer(num_str.offset(i), base) + }), + 8 => convert_octal(num_str), + 16 => convert_hex(num_str), + _ => convert_integer(num_str, base), + }; + + // check for error parsing octal/hex prefix + // also check to ensure a number was indeed parsed + let (num, i, overflow) = match res { + Some(res) => res, + None => { + invalid_input(); + return 0; + } + }; + idx += i; + + let overflow = if CHECK_SIGN { + overflow || (num as c_long).is_negative() + } else { + overflow + }; + // account for the sign + let num = num as $rettype; + let num = if overflow { + platform::errno = ERANGE; + if CHECK_SIGN { + if positive { + MAX_VAL + } else { + MIN_VAL + } + } else { + MAX_VAL + } + } else { + if positive { + num + } else { + // not using -num to keep the compiler happy + num.overflowing_neg().0 + } + }; + + set_endptr(idx); + + num + } + } } +strto_impl!( + strtoul, + c_ulong, + false, + c_ulong::max_value(), + c_ulong::min_value() +); +strto_impl!( + strtol, + c_long, + true, + c_long::max_value(), + c_long::min_value() +); + #[no_mangle] pub extern "C" fn system(command: *const c_char) -> c_int { unimplemented!(); @@ -711,10 +741,3 @@ pub extern "C" fn wcstombs(s: *mut c_char, pwcs: *const wchar_t, n: size_t) -> s pub extern "C" fn wctomb(s: *mut c_char, wchar: wchar_t) -> c_int { unimplemented!(); } - -/* -#[no_mangle] -pub extern "C" fn func(args) -> c_int { - unimplemented!(); -} -*/ diff --git a/tests/Makefile b/tests/Makefile index 1f83dd13946c6ac2cf800e584248467e7c2ee397..33b218c85426c4d401899902a80589406279d808 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -28,6 +28,7 @@ EXPECT_BINS=\ stdio/freopen \ stdlib/bsearch \ stdlib/strtol \ + stdlib/strtoul \ stdlib/a64l \ stdlib/rand \ string/strncmp \ diff --git a/tests/stdlib/strtoul.c b/tests/stdlib/strtoul.c new file mode 100644 index 0000000000000000000000000000000000000000..4431a720d231ef735c1b0d07d3264be4c9f38714 --- /dev/null +++ b/tests/stdlib/strtoul.c @@ -0,0 +1,30 @@ +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> + +int main(int argc, char* argv[]) { + printf("%ld\n", strtoul(" -42", NULL, 0)); + printf("%ld\n", strtoul(" +555", NULL, 0)); + printf("%ld\n", strtoul(" 1234567890 ", NULL, 0)); + + printf("%ld\n", strtoul(" -42", NULL, 10)); + printf("%ld\n", strtoul(" +555", NULL, 10)); + printf("%ld\n", strtoul(" 1234567890 ", NULL, 10)); + + printf("%lx\n", strtoul(" 0x38Acfg", NULL, 0)); + printf("%lx\n", strtoul("0Xabcdef12", NULL, 16)); + + printf("%lo\n", strtoul(" 073189", NULL, 0)); + printf("%lo\n", strtoul(" 073189", NULL, 8)); + + printf("%lo\n", strtoul(" 0b", NULL, 8)); + if(errno != 0) { + printf("errno is not 0 (%d), something went wrong\n", errno); + } + printf("%lo\n", strtoul(" 0b", NULL, 0)); + if(errno != 0) { + printf("errno is not 0 (%d), something went wrong\n", errno); + } + + return 0; +}