104 lines
2.9 KiB
Rust
104 lines
2.9 KiB
Rust
#[macro_export]
|
|
macro_rules! strto_impl {
|
|
(
|
|
$rettype:ty, $signed:expr, $maxval:expr, $minval:expr, $s:ident, $endptr:ident, $base:ident
|
|
) => {{
|
|
// 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() {
|
|
// This is stupid, but apparently strto* functions want
|
|
// const input but mut output, yet the man page says
|
|
// "stores the address of the first invalid character in *endptr"
|
|
// so obviously it doesn't want us to clone it.
|
|
*$endptr = $s.offset(idx) as *mut _;
|
|
}
|
|
};
|
|
|
|
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
|
|
}};
|
|
}
|