Skip to content
Snippets Groups Projects
Unverified Commit dd0e6187 authored by Jeremy Soller's avatar Jeremy Soller Committed by GitHub
Browse files

Merge pull request #50 from Arcterus/master

Preliminary implementation of strtol()
parents 580dcd3c b7ac90fd
No related branches found
No related tags found
No related merge requests found
......@@ -85,6 +85,13 @@ pub unsafe fn printf<W: fmt::Write>(mut w: W, format: *const c_char, mut ap: VaL
found_percent = false;
}
'o' => {
let a = ap.get::<c_uint>();
w.write_fmt(format_args!("{:o}", a));
found_percent = false;
}
'-' => {}
'+' => {}
' ' => {}
......
......@@ -416,8 +416,192 @@ pub unsafe extern "C" fn strtod(s: *const c_char, endptr: *mut *mut c_char) -> c
}
#[no_mangle]
pub extern "C" fn strtol(s: *const c_char, endptr: *mut *mut c_char, base: c_int) -> c_long {
unimplemented!();
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,
ch if ch == b'+' as c_char => Some((true, 1)),
ch if ch == b'-' as c_char => Some((false, 1)),
_ => Some((true, 0)),
}
}
fn detect_base(s: *const c_char) -> Option<(c_int, isize)> {
let first = unsafe { *s } as u8;
match first {
0 => None,
b'0' => {
let second = unsafe { *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)),
}
}
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
}
}
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 {
None
}
}
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 {
let val = unsafe { LOOKUP_TABLE[*s.offset(idx) 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 {
unsafe {
platform::errno = ERANGE;
}
num = c_ulong::max_value();
overflowed = true;
}
idx += 1;
}
}
if idx > 0 {
Some((num, idx, overflowed))
} else {
None
}
}
#[no_mangle]
......
......@@ -23,4 +23,5 @@
/pipe
/printf
/rmdir
/stdlib/strtol
/write
......@@ -19,6 +19,7 @@ BINS=\
rmdir \
pipe \
printf \
stdlib/strtol \
write
all: $(BINS)
......@@ -39,6 +40,7 @@ expected: $(BINS)
for bin in $(BINS); \
do \
echo "# $${bin} #"; \
mkdir -p expected/`dirname $${bin}`; \
"./$${bin}" test args > "expected/$${bin}.stdout" 2> "expected/$${bin}.stderr"; \
done
......@@ -48,6 +50,7 @@ verify: $(BINS)
for bin in $(BINS); \
do \
echo "# $${bin} #"; \
mkdir -p gen/`dirname $${bin}`; \
"./$${bin}" test args > "gen/$${bin}.stdout" 2> "gen/$${bin}.stderr"; \
diff -u "gen/$${bin}.stdout" "expected/$${bin}.stdout"; \
diff -u "gen/$${bin}.stderr" "expected/$${bin}.stderr"; \
......
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
printf("%ld\n", strtol(" -42", NULL, 0));
printf("%ld\n", strtol(" +555", NULL, 0));
printf("%ld\n", strtol(" 1234567890 ", NULL, 0));
printf("%ld\n", strtol(" -42", NULL, 10));
printf("%ld\n", strtol(" +555", NULL, 10));
printf("%ld\n", strtol(" 1234567890 ", NULL, 10));
printf("%lx\n", strtol(" 0x38Acfg", NULL, 0));
printf("%lx\n", strtol("0Xabcdef12", NULL, 16));
printf("%lo\n", strtol(" 073189", NULL, 0));
printf("%lo\n", strtol(" 073189", NULL, 8));
printf("%lo\n", strtol(" 0b", NULL, 8));
if(errno != 0) {
printf("errno is not 0 (%d), something went wrong\n", errno);
}
printf("%lo\n", strtol(" 0b", NULL, 0));
if(errno != 0) {
printf("errno is not 0 (%d), something went wrong\n", errno);
}
return 0;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment