From b9a2dfded43006e578327d46f29c31ce4a823d79 Mon Sep 17 00:00:00 2001 From: Paul Sajna Date: Fri, 9 Mar 2018 00:52:34 -0800 Subject: [PATCH 1/6] correction to include guard of stat --- src/stat/cbindgen.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stat/cbindgen.toml b/src/stat/cbindgen.toml index 8f75acc334..d4f38953db 100644 --- a/src/stat/cbindgen.toml +++ b/src/stat/cbindgen.toml @@ -1,5 +1,5 @@ sys_includes = ["sys/types.h"] -include_guard = "_STAT_H" +include_guard = "_SYS_STAT_H" language = "C" [enum] From a7e71717cb827ed2d1c34ac935bd868f6043569b Mon Sep 17 00:00:00 2001 From: Alex Lyon Date: Fri, 9 Mar 2018 02:47:33 -0800 Subject: [PATCH 2/6] stdio: add support for %o to printf() --- src/stdio/src/printf.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/stdio/src/printf.rs b/src/stdio/src/printf.rs index 54ce1f7644..093ee82c12 100644 --- a/src/stdio/src/printf.rs +++ b/src/stdio/src/printf.rs @@ -85,6 +85,13 @@ pub unsafe fn printf(mut w: W, format: *const c_char, mut ap: VaL found_percent = false; } + 'o' => { + let a = ap.get::(); + + w.write_fmt(format_args!("{:o}", a)); + + found_percent = false; + } '-' => {} '+' => {} ' ' => {} From f5b1f872a06c5dbae4704a6277e396256f5508d7 Mon Sep 17 00:00:00 2001 From: Alex Lyon Date: Fri, 9 Mar 2018 02:51:30 -0800 Subject: [PATCH 3/6] stdlib: implement preliminary version of strtol() --- src/stdlib/src/lib.rs | 194 +++++++++++++++++++++++++++++++++++++++++- tests/.gitignore | 1 + tests/Makefile | 1 + tests/stdlib/strtol.c | 30 +++++++ 4 files changed, 224 insertions(+), 2 deletions(-) create mode 100644 tests/stdlib/strtol.c diff --git a/src/stdlib/src/lib.rs b/src/stdlib/src/lib.rs index 1bc1ff4e46..4e6da79e15 100644 --- a/src/stdlib/src/lib.rs +++ b/src/stdlib/src/lib.rs @@ -416,8 +416,198 @@ 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 + 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] diff --git a/tests/.gitignore b/tests/.gitignore index 7b70bccffc..5e35eadf6c 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -23,4 +23,5 @@ /pipe /printf /rmdir +/stdlib/strtol /write diff --git a/tests/Makefile b/tests/Makefile index 6c2a838d87..e1cc0a79bd 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -19,6 +19,7 @@ BINS=\ rmdir \ pipe \ printf \ + stdlib/strtol \ write all: $(BINS) diff --git a/tests/stdlib/strtol.c b/tests/stdlib/strtol.c new file mode 100644 index 0000000000..7d09883c64 --- /dev/null +++ b/tests/stdlib/strtol.c @@ -0,0 +1,30 @@ +#include +#include +#include + +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; +} From 9325a29a746b188a1471086d1af60373dd48f45b Mon Sep 17 00:00:00 2001 From: Alex Lyon Date: Fri, 9 Mar 2018 03:21:18 -0800 Subject: [PATCH 4/6] stdlib: make rustfmt happy with strtol() --- src/stdlib/src/lib.rs | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/stdlib/src/lib.rs b/src/stdlib/src/lib.rs index 4e6da79e15..dea8dbd13a 100644 --- a/src/stdlib/src/lib.rs +++ b/src/stdlib/src/lib.rs @@ -416,7 +416,11 @@ 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 { +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); @@ -449,21 +453,17 @@ pub unsafe extern "C" fn strtol(s: *const c_char, endptr: *mut *const c_char, ba } None => { invalid_input(); - return 0 + 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) - }) - } + 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) + _ => convert_integer(num_str, base), }; // check for error parsing octal/hex prefix @@ -504,7 +504,7 @@ fn is_positive(ch: c_char) -> Option<(bool, isize)> { 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)) + _ => Some((true, 0)), } } @@ -523,9 +523,7 @@ fn detect_base(s: *const c_char) -> Option<(c_int, isize)> { Some((8, 0)) } } - _ => { - Some((10, 0)) - } + _ => Some((10, 0)), } } @@ -544,12 +542,9 @@ unsafe fn convert_octal(s: *const c_char) -> Option<(c_ulong, isize, bool)> { 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)) + && (*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) - }) + convert_integer(s.offset(2), 16).map(|(val, idx, overflow)| (val, idx + 2, overflow)) } else { None } @@ -557,6 +552,7 @@ unsafe fn convert_hex(s: *const c_char) -> Option<(c_ulong, isize, bool)> { 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, @@ -581,14 +577,12 @@ fn convert_integer(s: *const c_char, base: c_int) -> Option<(c_ulong, isize, boo let mut overflowed = false; loop { - let val = unsafe { - LOOKUP_TABLE[*s.offset(idx) as usize] - }; + 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)) + .and_then(|num| num.checked_add(val as c_ulong)) { num = res; } else { From dec7ecd06b19e541550d5bb3fcdf7c2644bd72e4 Mon Sep 17 00:00:00 2001 From: Alex Lyon Date: Fri, 9 Mar 2018 03:37:30 -0800 Subject: [PATCH 5/6] tests: fix Makefile --- tests/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/Makefile b/tests/Makefile index e1cc0a79bd..d1c3d111cb 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -29,7 +29,7 @@ clean: run: $(BINS) for bin in $(BINS); \ - do + do \ echo "# $${bin} #"; \ "./$${bin}" test args; \ done @@ -40,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 @@ -49,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"; \ From b11c079d69a1738ab69d4c4a8f8c68b6090b2f10 Mon Sep 17 00:00:00 2001 From: Paul Sajna Date: Fri, 9 Mar 2018 05:34:58 -0800 Subject: [PATCH 6/6] Makefile fix --- tests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Makefile b/tests/Makefile index 6c2a838d87..2dc88010f0 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -28,7 +28,7 @@ clean: run: $(BINS) for bin in $(BINS); \ - do + do \ echo "# $${bin} #"; \ "./$${bin}" test args; \ done