Merge pull request #121 from Arcterus/strtoul
stdlib: implement strtoul() and strtol() using a macro
This commit is contained in:
+119
-96
@@ -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!();
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -28,6 +28,7 @@ EXPECT_BINS=\
|
||||
stdio/freopen \
|
||||
stdlib/bsearch \
|
||||
stdlib/strtol \
|
||||
stdlib/strtoul \
|
||||
stdlib/a64l \
|
||||
stdlib/rand \
|
||||
string/strncmp \
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user