Browse Source

Merge branch 'master' of https://github.com/redox-os/relibc

MggMuggins 7 năm trước cách đây
mục cha
commit
06a6922cde
72 tập tin đã thay đổi với 638 bổ sung30 xóa
  1. 1 0
      .travis.yml
  2. 17 7
      Cargo.lock
  3. 1 0
      Cargo.toml
  4. 10 0
      README.md
  5. 1 1
      bindgen_transform.sh
  6. 6 0
      ci.sh
  7. 39 0
      include/sys/stat.h
  8. 10 0
      include/sys/types.h
  9. 1 0
      src/lib.rs
  10. 27 2
      src/platform/src/linux/mod.rs
  11. 35 0
      src/platform/src/redox/mod.rs
  12. 4 0
      src/platform/src/types.rs
  13. 11 0
      src/stat/Cargo.toml
  14. 13 0
      src/stat/build.rs
  15. 6 0
      src/stat/cbindgen.toml
  16. 75 0
      src/stat/src/lib.rs
  17. 15 5
      src/stdio/src/printf.rs
  18. 1 0
      src/stdlib/Cargo.toml
  19. 205 6
      src/stdlib/src/lib.rs
  20. 6 6
      src/unistd/src/lib.rs
  21. 7 0
      tests/.gitignore
  22. 32 1
      tests/Makefile
  23. 8 0
      tests/atof.c
  24. 0 0
      tests/expected/alloc.stderr
  25. 2 0
      tests/expected/alloc.stdout
  26. 0 0
      tests/expected/args.stderr
  27. 1 0
      tests/expected/args.stdout
  28. 0 0
      tests/expected/atof.stderr
  29. 1 0
      tests/expected/atof.stdout
  30. 0 0
      tests/expected/atoi.stderr
  31. 6 0
      tests/expected/atoi.stdout
  32. 0 0
      tests/expected/brk.stderr
  33. 1 0
      tests/expected/brk.stdout
  34. 0 0
      tests/expected/chdir.stderr
  35. 2 0
      tests/expected/chdir.stdout
  36. 0 0
      tests/expected/create.stderr
  37. 0 0
      tests/expected/create.stdout
  38. 0 0
      tests/expected/ctype.stderr
  39. 1 0
      tests/expected/ctype.stdout
  40. 0 0
      tests/expected/dup.stderr
  41. 1 0
      tests/expected/dup.stdout
  42. 1 0
      tests/expected/error.stderr
  43. 1 0
      tests/expected/error.stdout
  44. 0 0
      tests/expected/fchdir.stderr
  45. 1 0
      tests/expected/fchdir.stdout
  46. 0 0
      tests/expected/fcntl.stderr
  47. 1 0
      tests/expected/fcntl.stdout
  48. 0 0
      tests/expected/fsync.stderr
  49. 1 0
      tests/expected/fsync.stdout
  50. 0 0
      tests/expected/ftruncate.stderr
  51. 1 0
      tests/expected/ftruncate.stdout
  52. 0 0
      tests/expected/getid.stderr
  53. 1 0
      tests/expected/getid.stdout
  54. 0 0
      tests/expected/link.stderr
  55. 0 0
      tests/expected/link.stdout
  56. 0 0
      tests/expected/math.stderr
  57. 0 0
      tests/expected/math.stdout
  58. 0 0
      tests/expected/pipe.stderr
  59. 0 0
      tests/expected/pipe.stdout
  60. 0 0
      tests/expected/printf.stderr
  61. 8 0
      tests/expected/printf.stdout
  62. 0 0
      tests/expected/rmdir.stderr
  63. 1 0
      tests/expected/rmdir.stdout
  64. 0 0
      tests/expected/write.stderr
  65. 1 0
      tests/expected/write.stdout
  66. BIN
      tests/fcntl
  67. 2 1
      tests/link.c
  68. 9 0
      tests/rmdir.c
  69. 26 0
      tests/setid.c
  70. 30 0
      tests/stdlib/strtol.c
  71. 7 0
      tests/unlink.c
  72. 1 1
      va_list

+ 1 - 0
.travis.yml

@@ -5,6 +5,7 @@ cache: cargo
 before_script:
   - rustup component add rustfmt-preview
   - rustup target add x86_64-unknown-redox
+  - rustup target add aarch64-unknown-linux-gnu
 script:
   - bash ./ci.sh
 notifications:

+ 17 - 7
Cargo.lock

@@ -31,6 +31,11 @@ dependencies = [
  "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "cc"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "cfg-if"
 version = "0.1.2"
@@ -105,11 +110,6 @@ name = "fuchsia-zircon-sys"
 version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
-[[package]]
-name = "gcc"
-version = "0.3.54"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
 [[package]]
 name = "grp"
 version = "0.1.0"
@@ -258,6 +258,7 @@ dependencies = [
  "mman 0.1.0",
  "platform 0.1.0",
  "semaphore 0.1.0",
+ "stat 0.1.0",
  "stdio 0.1.0",
  "stdlib 0.1.0",
  "string 0.1.0",
@@ -440,6 +441,14 @@ dependencies = [
  "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "stat"
+version = "0.1.0"
+dependencies = [
+ "cbindgen 0.5.0",
+ "platform 0.1.0",
+]
+
 [[package]]
 name = "stdio"
 version = "0.1.0"
@@ -456,6 +465,7 @@ version = "0.1.0"
 dependencies = [
  "cbindgen 0.5.0",
  "ctype 0.1.0",
+ "errno 0.1.0",
  "platform 0.1.0",
  "ralloc 1.0.0",
 ]
@@ -576,7 +586,7 @@ dependencies = [
 name = "va_list-helper"
 version = "0.0.2"
 dependencies = [
- "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -625,13 +635,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455"
 "checksum atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8352656fd42c30a0c3c89d26dea01e3b77c0ab2af18230835c15e2e13cd51859"
 "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
+"checksum cc 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9be26b24e988625409b19736d130f0c7d224f01d06454b5f81d8d23d6c1a618f"
 "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
 "checksum clap 2.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1c07b9257a00f3fc93b7f3c417fc15607ec7a56823bc2c37ec744e266387de5b"
 "checksum compiler_builtins 0.1.0 (git+https://github.com/rust-lang-nursery/compiler-builtins.git)" = "<none>"
 "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
 "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
 "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
-"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb"
 "checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c"
 "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
 "checksum libc 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)" = "56aebce561378d99a0bb578f8cb15b6114d2a1814a6c7949bbe646d968bb4fa9"

+ 1 - 0
Cargo.toml

@@ -19,6 +19,7 @@ fcntl = { path = "src/fcntl" }
 grp = { path = "src/grp" }
 semaphore = { path = "src/semaphore" }
 mman = { path = "src/mman" }
+stat = { path = "src/stat" }
 stdio = { path = "src/stdio" }
 stdlib = { path = "src/stdlib" }
 string = { path = "src/string" }

+ 10 - 0
README.md

@@ -5,3 +5,13 @@ The motivation for this project is twofold: Reduce issues the redox crew was hav
 
 ## Contributing
 Just search for any invocation of the `unimplemented` macro, and hop in! The ci server checks builds for linux and redox, checks formatting (via rustfmt), and runs the test suite. Run `ci.sh` locally to check that your changes will pass travis. Use `fmt.sh` to format your code and `test.sh` to run the C test suite.
+
+## Supported OSes
+
+ - RedoxOS
+ - Linux
+
+## Supported architectures
+
+ - x86\_64
+ - Aarch64

+ 1 - 1
bindgen_transform.sh

@@ -1,4 +1,4 @@
-sed -i 's/::std::os::raw::/libc::/g' $1
+sed -i 's/::std::os::raw:://g' $1
 perl -i -p0e 's/extern "C" \{\n    pub fn/#[no_mangle]\npub extern "C" fn/g' $1 
 perl -i -p0e 's/;\n\}/ {\n    unimplemented!();\n\}\n/g' $1
 rustfmt $1

+ 6 - 0
ci.sh

@@ -4,3 +4,9 @@ set -ex
 ./fmt.sh -- --write-mode=diff
 ./test.sh
 cargo build --target=x86_64-unknown-redox
+if [ $(arch) == "x86_64" ]
+then
+    cargo build --target=aarch64-unknown-linux-gnu
+else
+    cargo build --target=x86_64-unknown-linux-gnu
+fi

+ 39 - 0
include/sys/stat.h

@@ -0,0 +1,39 @@
+#ifndef _STAT_H
+#define _STAT_H
+
+#include <sys/types.h>
+
+struct stat {
+  dev_t st_dev;
+  ino_t st_ino;
+  nlink_t st_nlink;
+  mode_t st_mode;
+  uid_t st_uid;
+  gid_t st_gid;
+  dev_t st_rdev;
+  off_t st_size;
+  blksize_t st_blksize;
+  time_t st_atim;
+  time_t st_mtim;
+  time_t st_ctim;
+};
+
+int chmod(const char *path, mode_t mode);
+
+int fchmod(int fildes, mode_t mode);
+
+int fstat(int fildes, struct stat *buf);
+
+int lstat(const char *path, struct stat *buf);
+
+int mkdir(const char *path, mode_t mode);
+
+int mkfifo(const char *path, mode_t mode);
+
+int mknod(const char *path, mode_t mode, dev_t dev);
+
+int stat(const char *file, struct stat *buf);
+
+mode_t umask(mode_t mask);
+
+#endif /* _STAT_H */

+ 10 - 0
include/sys/types.h

@@ -1,17 +1,27 @@
 #ifndef _SYS_TYPES_H
 #define _SYS_TYPES_H
 
+typedef long blksize_t;
+
+typedef long dev_t;
+
+typedef unsigned long ino_t;
+
 typedef int gid_t;
 typedef int uid_t;
 
 typedef int mode_t;
 
+typedef unsigned long nlink_t;
+
 typedef long off_t;
 
 typedef int pid_t;
 
 typedef long ssize_t;
 
+typedef long time_t;
+
 typedef int useconds_t;
 
 #endif /* _SYS_TYPES_H */

+ 1 - 0
src/lib.rs

@@ -10,6 +10,7 @@ extern crate fcntl;
 extern crate grp;
 extern crate mman;
 extern crate semaphore;
+extern crate stat;
 extern crate stdio;
 extern crate stdlib;
 extern crate string;

+ 27 - 2
src/platform/src/linux/mod.rs

@@ -4,6 +4,7 @@ use errno;
 use types::*;
 
 const AT_FDCWD: c_int = -100;
+const AT_REMOVEDIR: c_int = 0x200;
 
 pub fn e(sys: usize) -> usize {
     if (sys as isize) < 0 && (sys as isize) >= -256 {
@@ -67,7 +68,7 @@ pub fn fcntl(fildes: c_int, cmd: c_int, arg: c_int) -> c_int {
 }
 
 pub fn fork() -> pid_t {
-    e(unsafe { syscall!(FORK) }) as pid_t
+    e(unsafe { syscall!(CLONE, 17, 0) }) as pid_t
 }
 
 pub fn fsync(fildes: c_int) -> c_int {
@@ -115,7 +116,11 @@ pub fn getuid() -> uid_t {
 }
 
 pub fn link(path1: *const c_char, path2: *const c_char) -> c_int {
-    e(unsafe { syscall!(LINKAT, AT_FDCWD, path1, path2) }) as c_int
+    e(unsafe { syscall!(LINKAT, AT_FDCWD, path1, AT_FDCWD, path2, 0) }) as c_int
+}
+
+pub fn mkdir(path: *const c_char, mode: mode_t) -> c_int {
+    e(unsafe { syscall!(MKDIRAT, AT_FDCWD, path, mode) }) as c_int
 }
 
 pub fn open(path: *const c_char, oflag: c_int, mode: mode_t) -> c_int {
@@ -130,6 +135,26 @@ pub fn read(fildes: c_int, buf: &mut [u8]) -> ssize_t {
     e(unsafe { syscall!(READ, fildes, buf.as_mut_ptr(), buf.len()) }) as ssize_t
 }
 
+pub fn rmdir(path: *const c_char) -> c_int {
+    e(unsafe { syscall!(UNLINKAT, AT_FDCWD, path, AT_REMOVEDIR) }) as c_int
+}
+
+pub fn setpgid(pid: pid_t, pgid: pid_t) -> c_int {
+    e(unsafe { syscall!(SETPGID, pid, pgid) }) as c_int
+}
+
+pub fn setregid(rgid: gid_t, egid: gid_t) -> c_int {
+    e(unsafe { syscall!(SETREGID, rgid, egid) }) as c_int
+}
+
+pub fn setreuid(ruid: uid_t, euid: uid_t) -> c_int {
+    e(unsafe { syscall!(SETREUID, ruid, euid) }) as c_int
+}
+
+pub fn unlink(path: *const c_char) -> c_int {
+    e(unsafe { syscall!(UNLINKAT, AT_FDCWD, path, 0) }) as c_int
+}
+
 pub fn write(fildes: c_int, buf: &[u8]) -> ssize_t {
     e(unsafe { syscall!(WRITE, fildes, buf.as_ptr(), buf.len()) }) as ssize_t
 }

+ 35 - 0
src/platform/src/redox/mod.rs

@@ -1,6 +1,7 @@
 use core::ptr;
 use core::slice;
 use syscall;
+use syscall::flag::*;
 
 use c_str;
 use errno;
@@ -122,6 +123,18 @@ pub fn link(path1: *const c_char, path2: *const c_char) -> c_int {
     e(unsafe { syscall::link(path1.as_ptr(), path2.as_ptr()) }) as c_int
 }
 
+pub fn mkdir(path: *const c_char, mode: mode_t) -> c_int {
+    let flags = O_CREAT | O_EXCL | O_CLOEXEC | O_DIRECTORY | mode as usize & 0o777;
+    let path = unsafe { c_str(path) };
+    match syscall::open(path, flags) {
+        Ok(fd) => {
+            syscall::close(fd);
+            0
+        }
+        Err(err) => e(Err(err)) as c_int,
+    }
+}
+
 pub fn open(path: *const c_char, oflag: c_int, mode: mode_t) -> c_int {
     let path = unsafe { c_str(path) };
     e(syscall::open(path, (oflag as usize) | (mode as usize))) as c_int
@@ -139,6 +152,28 @@ pub fn read(fd: c_int, buf: &mut [u8]) -> ssize_t {
     e(syscall::read(fd as usize, buf)) as ssize_t
 }
 
+pub fn rmdir(path: *const c_char) -> c_int {
+    let path = unsafe { c_str(path) };
+    e(syscall::rmdir(path)) as c_int
+}
+
+pub fn setpgid(pid: pid_t, pgid: pid_t) -> c_int {
+    e(syscall::setpgid(pid as usize, pgid as usize)) as c_int
+}
+
+pub fn setregid(rgid: gid_t, egid: gid_t) -> c_int {
+    e(syscall::setregid(rgid as usize, egid as usize)) as c_int
+}
+
+pub fn setreuid(ruid: uid_t, euid: uid_t) -> c_int {
+    e(syscall::setreuid(ruid as usize, euid as usize)) as c_int
+}
+
+pub fn unlink(path: *const c_char) -> c_int {
+    let path = unsafe { c_str(path) };
+    e(syscall::unlink(path)) as c_int
+}
+
 pub fn write(fd: c_int, buf: &[u8]) -> ssize_t {
     e(syscall::write(fd as usize, buf)) as ssize_t
 }

+ 4 - 0
src/platform/src/types.rs

@@ -52,6 +52,10 @@ pub type time_t = i64;
 pub type pid_t = usize;
 pub type gid_t = usize;
 pub type uid_t = usize;
+pub type dev_t = usize;
+pub type ino_t = usize;
+pub type nlink_t = usize;
+pub type blksize_t = isize;
 
 pub type useconds_t = i32;
 pub type suseconds_t = i64;

+ 11 - 0
src/stat/Cargo.toml

@@ -0,0 +1,11 @@
+[package]
+name = "stat"
+version = "0.1.0"
+authors = ["Jeremy Soller <jackpot51@gmail.com>"]
+build = "build.rs"
+
+[build-dependencies]
+cbindgen = { path = "../../cbindgen" }
+
+[dependencies]
+platform = { path = "../platform" }

+ 13 - 0
src/stat/build.rs

@@ -0,0 +1,13 @@
+extern crate cbindgen;
+
+use std::{env, fs};
+
+fn main() {
+    /*
+     *let crate_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");
+     *fs::create_dir_all("../../target/include").expect("failed to create include directory");
+     *cbindgen::generate(crate_dir)
+     *  .expect("failed to generate bindings")
+     *  .write_to_file("../../target/include/sys/stat.h");
+     */
+}

+ 6 - 0
src/stat/cbindgen.toml

@@ -0,0 +1,6 @@
+sys_includes = ["sys/types.h"]
+include_guard = "_SYS_STAT_H"
+language = "C"
+
+[enum]
+prefix_with_name = true

+ 75 - 0
src/stat/src/lib.rs

@@ -0,0 +1,75 @@
+//! stat implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/sysstat.h.html
+
+#![no_std]
+
+extern crate platform;
+
+use platform::types::*;
+
+#[repr(C)]
+pub struct stat {
+    pub st_dev: dev_t,
+    pub st_ino: ino_t,
+    pub st_nlink: nlink_t,
+    pub st_mode: mode_t,
+    pub st_uid: uid_t,
+    pub st_gid: gid_t,
+    pub st_rdev: dev_t,
+    pub st_size: off_t,
+    pub st_blksize: blksize_t,
+    pub st_atim: time_t,
+    pub st_mtim: time_t,
+    pub st_ctim: time_t,
+}
+
+#[no_mangle]
+pub extern "C" fn chmod(path: *const c_char, mode: mode_t) -> c_int {
+    unimplemented!();
+}
+
+#[no_mangle]
+pub extern "C" fn fchmod(fildes: c_int, mode: mode_t) -> c_int {
+    unimplemented!();
+}
+
+#[no_mangle]
+pub extern "C" fn fstat(fildes: c_int, buf: *mut stat) -> c_int {
+    unimplemented!();
+}
+
+#[no_mangle]
+pub extern "C" fn lstat(path: *const c_char, buf: *mut stat) -> c_int {
+    unimplemented!();
+}
+
+#[no_mangle]
+pub extern "C" fn mkdir(path: *const c_char, mode: mode_t) -> c_int {
+    platform::mkdir(path, mode)
+}
+
+#[no_mangle]
+pub extern "C" fn mkfifo(path: *const c_char, mode: mode_t) -> c_int {
+    unimplemented!();
+}
+
+#[no_mangle]
+pub extern "C" fn mknod(path: *const c_char, mode: mode_t, dev: dev_t) -> c_int {
+    unimplemented!();
+}
+
+#[no_mangle]
+pub extern "C" fn stat(file: *const c_char, buf: *mut stat) -> c_int {
+    unimplemented!();
+}
+
+#[no_mangle]
+pub extern "C" fn umask(mask: mode_t) -> mode_t {
+    unimplemented!();
+}
+
+/*
+#[no_mangle]
+pub extern "C" fn func(args) -> c_int {
+    unimplemented!();
+}
+*/

+ 15 - 5
src/stdio/src/printf.rs

@@ -1,13 +1,9 @@
-use core::fmt;
+use core::{fmt, mem, slice, str};
 
 use platform::types::*;
 use vl::VaList;
 
 pub unsafe fn printf<W: fmt::Write>(mut w: W, format: *const c_char, mut ap: VaList) -> c_int {
-    use core::fmt::Write;
-    use core::slice;
-    use core::str;
-
     extern "C" {
         fn strlen(s: *const c_char) -> size_t;
     }
@@ -39,6 +35,13 @@ pub unsafe fn printf<W: fmt::Write>(mut w: W, format: *const c_char, mut ap: VaL
 
                     found_percent = false;
                 }
+                'f' | 'F' => {
+                    let a = ap.get::<f64>();
+
+                    w.write_fmt(format_args!("{}", a));
+
+                    found_percent = false;
+                }
                 'n' => {
                     let _a = ap.get::<c_int>();
 
@@ -82,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;
+                }
                 '-' => {}
                 '+' => {}
                 ' ' => {}

+ 1 - 0
src/stdlib/Cargo.toml

@@ -11,3 +11,4 @@ cbindgen = { path = "../../cbindgen" }
 platform = { path = "../platform" }
 ralloc = { path = "../../ralloc", default-features = false }
 ctype = { path = "../ctype" }
+errno = { path = "../errno" }

+ 205 - 6
src/stdlib/src/lib.rs

@@ -5,9 +5,13 @@
 #![feature(global_allocator)]
 
 extern crate ctype;
+extern crate errno;
 extern crate platform;
 extern crate ralloc;
 
+use core::{ptr, str};
+
+use errno::*;
 use platform::types::*;
 
 #[global_allocator]
@@ -52,8 +56,8 @@ pub unsafe extern "C" fn atexit(func: Option<extern "C" fn()>) -> c_int {
 }
 
 #[no_mangle]
-pub extern "C" fn atof(s: *const c_char) -> c_double {
-    unimplemented!();
+pub unsafe extern "C" fn atof(s: *const c_char) -> c_double {
+    strtod(s, ptr::null_mut())
 }
 
 macro_rules! dec_num_from_ascii {
@@ -396,13 +400,208 @@ pub extern "C" fn srandom(seed: c_uint) {
 }
 
 #[no_mangle]
-pub extern "C" fn strtod(s: *const c_char, endptr: *mut *mut c_char) -> c_double {
-    unimplemented!();
+pub unsafe extern "C" fn strtod(s: *const c_char, endptr: *mut *mut c_char) -> c_double {
+    //TODO: endptr
+
+    use core::str::FromStr;
+
+    let s_str = str::from_utf8_unchecked(platform::c_str(s));
+    match f64::from_str(s_str) {
+        Ok(ok) => ok as c_double,
+        Err(_err) => {
+            platform::errno = EINVAL;
+            0.0
+        }
+    }
 }
 
 #[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]

+ 6 - 6
src/unistd/src/lib.rs

@@ -225,7 +225,7 @@ pub extern "C" fn getpgid(pid: pid_t) -> pid_t {
 
 #[no_mangle]
 pub extern "C" fn getpgrp() -> pid_t {
-    unimplemented!();
+    platform::getpgid(platform::getpid())
 }
 
 #[no_mangle]
@@ -336,7 +336,7 @@ pub extern "C" fn readlink(path: *const c_char, buf: *mut c_char, bufsize: size_
 
 #[no_mangle]
 pub extern "C" fn rmdir(path: *const c_char) -> c_int {
-    unimplemented!();
+    platform::rmdir(path)
 }
 
 #[no_mangle]
@@ -351,7 +351,7 @@ pub extern "C" fn setgid(gid: gid_t) -> c_int {
 
 #[no_mangle]
 pub extern "C" fn setpgid(pid: pid_t, pgid: pid_t) -> c_int {
-    unimplemented!();
+    platform::setpgid(pid, pgid)
 }
 
 #[no_mangle]
@@ -361,12 +361,12 @@ pub extern "C" fn setpgrp() -> pid_t {
 
 #[no_mangle]
 pub extern "C" fn setregid(rgid: gid_t, egid: gid_t) -> c_int {
-    unimplemented!();
+    platform::setregid(rgid, egid)
 }
 
 #[no_mangle]
 pub extern "C" fn setreuid(ruid: uid_t, euid: uid_t) -> c_int {
-    unimplemented!();
+    platform::setreuid(ruid, euid)
 }
 
 #[no_mangle]
@@ -436,7 +436,7 @@ pub extern "C" fn ualarm(useconds: useconds_t, interval: useconds_t) -> useconds
 
 #[no_mangle]
 pub extern "C" fn unlink(path: *const c_char) -> c_int {
-    unimplemented!();
+    platform::unlink(path)
 }
 
 #[no_mangle]

+ 7 - 0
tests/.gitignore

@@ -1,5 +1,7 @@
+/gen/
 /alloc
 /args
+/atof
 /atoi
 /brk
 /chdir
@@ -10,6 +12,7 @@
 /dup.out
 /error
 /fchdir
+/fcntl
 /fsync
 /ftruncate
 /ftruncate.out
@@ -19,4 +22,8 @@
 /math
 /pipe
 /printf
+/rmdir
+/setid
+/unlink
+/stdlib/strtol
 /write

+ 32 - 1
tests/Makefile

@@ -1,5 +1,6 @@
 BINS=\
 	alloc \
+	atof \
 	atoi \
 	brk \
 	args \
@@ -17,6 +18,10 @@ BINS=\
 	math \
 	pipe \
 	printf \
+	rmdir \
+	setid \
+	unlink \
+	stdlib/strtol \
 	write
 
 all: $(BINS)
@@ -25,7 +30,33 @@ clean:
 	rm -f $(BINS) *.out
 
 run: $(BINS)
-	for bin in $(BINS); do echo "# $${bin} #"; "./$${bin}" test args; done
+	for bin in $(BINS); \
+	do \
+		echo "# $${bin} #"; \
+		"./$${bin}" test args; \
+	done
+
+expected: $(BINS)
+	rm -rf expected
+	mkdir -p expected
+	for bin in $(BINS); \
+	do \
+		echo "# $${bin} #"; \
+		mkdir -p expected/`dirname $${bin}`; \
+		"./$${bin}" test args > "expected/$${bin}.stdout" 2> "expected/$${bin}.stderr"; \
+	done
+
+verify: $(BINS)
+	rm -rf gen
+	mkdir -p gen
+	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"; \
+	done
 
 GCCHEAD=\
 	-nostdinc \

+ 8 - 0
tests/atof.c

@@ -0,0 +1,8 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+int main(int argc, char* argv[]) {
+    double d = atof("-3.14");
+    printf("%f\n", d);
+    return 0;
+}

+ 0 - 0
tests/expected/alloc.stderr


+ 2 - 0
tests/expected/alloc.stdout

@@ -0,0 +1,2 @@
+malloc 0x55ac6c472618
+calloc 0x55ac6c472618

+ 0 - 0
tests/expected/args.stderr


+ 1 - 0
tests/expected/args.stdout

@@ -0,0 +1 @@
+./args test args 

+ 0 - 0
tests/expected/atof.stderr


+ 1 - 0
tests/expected/atof.stdout

@@ -0,0 +1 @@
+-3.14

+ 0 - 0
tests/expected/atoi.stderr


+ 6 - 0
tests/expected/atoi.stdout

@@ -0,0 +1,6 @@
+-42
+555
+1234567890
+-42
+555
+1234567890

+ 0 - 0
tests/expected/brk.stderr


+ 1 - 0
tests/expected/brk.stdout

@@ -0,0 +1 @@
+brk exited with status code 0

+ 0 - 0
tests/expected/chdir.stderr


+ 2 - 0
tests/expected/chdir.stdout

@@ -0,0 +1,2 @@
+initial cwd: /home/jeremy/Projects/relibc/tests
+final cwd: /home/jeremy/Projects/relibc

+ 0 - 0
tests/expected/create.stderr


+ 0 - 0
tests/expected/create.stdout


+ 0 - 0
tests/expected/ctype.stderr


+ 1 - 0
tests/expected/ctype.stdout

@@ -0,0 +1 @@
+Success: 0

+ 0 - 0
tests/expected/dup.stderr


+ 1 - 0
tests/expected/dup.stdout

@@ -0,0 +1 @@
+fd 4 duped into fd 5

+ 1 - 0
tests/expected/error.stderr

@@ -0,0 +1 @@
+perror: No such file or directory

+ 1 - 0
tests/expected/error.stdout

@@ -0,0 +1 @@
+errno: 2 = No such file or directory

+ 0 - 0
tests/expected/fchdir.stderr


+ 1 - 0
tests/expected/fchdir.stdout

@@ -0,0 +1 @@
+fchdir exited with status code 0

+ 0 - 0
tests/expected/fcntl.stderr


+ 1 - 0
tests/expected/fcntl.stdout

@@ -0,0 +1 @@
+fd 4 duped into fd 5

+ 0 - 0
tests/expected/fsync.stderr


+ 1 - 0
tests/expected/fsync.stdout

@@ -0,0 +1 @@
+fsync exited with status code 0

+ 0 - 0
tests/expected/ftruncate.stderr


+ 1 - 0
tests/expected/ftruncate.stdout

@@ -0,0 +1 @@
+ftruncate exited with status code 0

+ 0 - 0
tests/expected/getid.stderr


+ 1 - 0
tests/expected/getid.stdout

@@ -0,0 +1 @@
+egid: 1000, euid: 1000, gid: 1000, pgid: 23916, pid: 23933, ppid 23918, uid 1000

+ 0 - 0
tests/expected/link.stderr


+ 0 - 0
tests/expected/link.stdout


+ 0 - 0
tests/expected/math.stderr


+ 0 - 0
tests/expected/math.stdout


+ 0 - 0
tests/expected/pipe.stderr


+ 0 - 0
tests/expected/pipe.stdout


+ 0 - 0
tests/expected/printf.stderr


+ 8 - 0
tests/expected/printf.stdout

@@ -0,0 +1,8 @@
+percent: %
+string: String
+char: c
+int: -16
+uint: 32
+hex: beef
+HEX: C0FFEE
+string: end

+ 0 - 0
tests/expected/rmdir.stderr


+ 1 - 0
tests/expected/rmdir.stdout

@@ -0,0 +1 @@
+rmdir exited with status code 0

+ 0 - 0
tests/expected/write.stderr


+ 1 - 0
tests/expected/write.stdout

@@ -0,0 +1 @@
+Hello World!

BIN
tests/fcntl


+ 2 - 1
tests/link.c

@@ -1,5 +1,6 @@
 #include <unistd.h>
 
 int main(int argc, char** argv) {
-    int status = link("link.c", "link.out");
+    link("./link.c", "./link.out");
+    perror("link");
 }

+ 9 - 0
tests/rmdir.c

@@ -0,0 +1,9 @@
+#include <unistd.h>
+#include <sys/stat.h>
+#include <stdio.h>
+
+int main(int argc, char** argv) {
+    mkdir("foo", 0);
+    int status = rmdir("foo");
+    printf("rmdir exited with status code %d\n", status);
+}

+ 26 - 0
tests/setid.c

@@ -0,0 +1,26 @@
+/*
+ * The process joins process group 0.
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+int main( void )
+  {
+    if( setpgid( getpid(), 0 ) == -1 ) {
+        perror( "setpgid" );
+    }
+    printf( "%d belongs to process group %d\n",
+         getpid(), getpgrp() );
+
+    if( setregid(-1, -1) == -1 ) {
+        perror( "setregid" );
+    }
+    printf("%d has egid %d and gid %d\n", getpid(), getegid(), getgid());
+
+    if( setreuid(-1, -1) == -1 ) {
+        perror( "setreuid" );
+    }
+    printf("%d has euid %d and uid %d\n", getpid(), geteuid(), getuid());
+  }

+ 30 - 0
tests/stdlib/strtol.c

@@ -0,0 +1,30 @@
+#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;
+}

+ 7 - 0
tests/unlink.c

@@ -0,0 +1,7 @@
+#include <unistd.h>
+#include <stdio.h>
+
+int main(int argc, char** argv) {
+    link("./unlink.c", "./unlink.out");
+    perror("unlink");
+}

+ 1 - 1
va_list

@@ -1 +1 @@
-Subproject commit 4762a184501beedbb58ea218f0c84fea85685c35
+Subproject commit ba5a07a744915b501acadb17e05db0f5d6552f72