about summary refs log tree commit diff
path: root/src/libcore
diff options
context:
space:
mode:
authorGraydon Hoare <graydon@mozilla.com>2012-03-12 20:04:27 -0700
committerGraydon Hoare <graydon@mozilla.com>2012-03-12 20:08:29 -0700
commit6f5853f5a1767e0c418fd5f348a795b76d701b3e (patch)
tree7d054b8471a75c47854edbd52a14d8d2acf81c9d /src/libcore
parentac57bb38560fa35d883505af2e8e68498b436fe7 (diff)
downloadrust-6f5853f5a1767e0c418fd5f348a795b76d701b3e.tar.gz
rust-6f5853f5a1767e0c418fd5f348a795b76d701b3e.zip
Libc/os/run/rand/io reorganization. Close #1373. Close #1638.
 - Move io, run and rand to core.
 - Remove incorrect ctypes module (use libc).
 - Remove os-specific modules for os and fs.
 - Split fs between core::path and core::os.
Diffstat (limited to 'src/libcore')
-rw-r--r--src/libcore/cmath.rs6
-rw-r--r--src/libcore/comm.rs22
-rw-r--r--src/libcore/core.rc6
-rw-r--r--src/libcore/io.rs776
-rw-r--r--src/libcore/libc.rs37
-rw-r--r--src/libcore/os.rs151
-rw-r--r--src/libcore/ptr.rs6
-rw-r--r--src/libcore/rand.rs113
-rw-r--r--src/libcore/run.rs394
-rw-r--r--src/libcore/str.rs2
-rw-r--r--src/libcore/sys.rs16
-rw-r--r--src/libcore/task.rs18
-rw-r--r--src/libcore/vec.rs8
13 files changed, 1472 insertions, 83 deletions
diff --git a/src/libcore/cmath.rs b/src/libcore/cmath.rs
index e7761a9b383..ba2bad81906 100644
--- a/src/libcore/cmath.rs
+++ b/src/libcore/cmath.rs
@@ -7,9 +7,9 @@ export c_double;
 export c_float_targ_consts;
 export c_double_targ_consts;
 
-import ctypes::c_int;
-import ctypes::c_float;
-import ctypes::c_double;
+import libc::c_int;
+import libc::c_float;
+import libc::c_double;
 
 // function names are almost identical to C's libmath, a few have been
 // renamed, grep for "rename:"
diff --git a/src/libcore/comm.rs b/src/libcore/comm.rs
index 413144aff69..e4f0663c443 100644
--- a/src/libcore/comm.rs
+++ b/src/libcore/comm.rs
@@ -41,20 +41,20 @@ native mod rustrt {
     fn get_task_id() -> task_id;
     fn chan_id_send<T: send>(t: *sys::type_desc,
                             target_task: task_id, target_port: port_id,
-                            data: T) -> ctypes::uintptr_t;
+                            data: T) -> libc::uintptr_t;
 
-    fn new_port(unit_sz: ctypes::size_t) -> *rust_port;
+    fn new_port(unit_sz: libc::size_t) -> *rust_port;
     fn del_port(po: *rust_port);
     fn rust_port_begin_detach(po: *rust_port,
-                              yield: *ctypes::uintptr_t);
+                              yield: *libc::uintptr_t);
     fn rust_port_end_detach(po: *rust_port);
     fn get_port_id(po: *rust_port) -> port_id;
-    fn rust_port_size(po: *rust_port) -> ctypes::size_t;
+    fn rust_port_size(po: *rust_port) -> libc::size_t;
     fn port_recv(dptr: *uint, po: *rust_port,
-                 yield: *ctypes::uintptr_t);
+                 yield: *libc::uintptr_t);
     fn rust_port_select(dptr: **rust_port, ports: **rust_port,
-                        n_ports: ctypes::size_t,
-                        yield: *ctypes::uintptr_t);
+                        n_ports: libc::size_t,
+                        yield: *libc::uintptr_t);
 }
 
 #[abi = "rust-intrinsic"]
@@ -147,7 +147,7 @@ fn recv_<T: send>(p: *rust_port) -> T {
     // that will grab the value of the return pointer, then call this
     // function, which we will then use to call the runtime.
     fn recv(dptr: *uint, port: *rust_port,
-            yield: *ctypes::uintptr_t) unsafe {
+            yield: *libc::uintptr_t) unsafe {
         rustrt::port_recv(dptr, port, yield);
     }
     let yield = 0u;
@@ -170,13 +170,13 @@ fn select2<A: send, B: send>(
 ) -> either::t<A, B> unsafe {
 
     fn select(dptr: **rust_port, ports: **rust_port,
-              n_ports: ctypes::size_t, yield: *ctypes::uintptr_t) {
+              n_ports: libc::size_t, yield: *libc::uintptr_t) {
         rustrt::rust_port_select(dptr, ports, n_ports, yield)
     }
 
     let mut ports = [];
     ports += [***p_a, ***p_b];
-    let n_ports = 2 as ctypes::size_t;
+    let n_ports = 2 as libc::size_t;
     let yield = 0u;
     let yieldp = ptr::addr_of(yield);
 
@@ -209,7 +209,7 @@ fn select2<A: send, B: send>(
 
 #[doc = "Returns true if there are messages available"]
 fn peek<T: send>(p: port<T>) -> bool {
-    rustrt::rust_port_size(***p) != 0u as ctypes::size_t
+    rustrt::rust_port_size(***p) != 0u as libc::size_t
 }
 
 #[doc = "
diff --git a/src/libcore/core.rc b/src/libcore/core.rc
index c7a85243e25..c28ad0a0282 100644
--- a/src/libcore/core.rc
+++ b/src/libcore/core.rc
@@ -31,7 +31,7 @@ export uint, u8, u16, u32, u64;
 export float, f32, f64;
 export box, char, str, ptr, vec, bool;
 export either, option, result, iter;
-export libc, os, ctypes, sys, unsafe, logging;
+export libc, os, io, run, rand, sys, unsafe, logging;
 export comm, task, future;
 export extfmt;
 export tuple;
@@ -79,9 +79,11 @@ mod to_str;
 
 mod libc;
 mod os;
+mod io;
+mod run;
+mod rand;
 mod path;
 
-mod ctypes;
 mod cmath;
 mod sys;
 mod unsafe;
diff --git a/src/libcore/io.rs b/src/libcore/io.rs
new file mode 100644
index 00000000000..23974b789c3
--- /dev/null
+++ b/src/libcore/io.rs
@@ -0,0 +1,776 @@
+/*
+Module: io
+
+Basic input/output
+*/
+
+import libc::{c_int, c_uint, c_void, size_t, ssize_t};
+import libc::consts::os::posix88::*;
+import libc::consts::os::extra::*;
+
+type fd_t = c_int;
+
+#[abi = "cdecl"]
+native mod rustrt {
+    fn rust_get_stdin() -> *libc::FILE;
+    fn rust_get_stdout() -> *libc::FILE;
+    fn rust_get_stderr() -> *libc::FILE;
+}
+
+// Reading
+
+// FIXME This is all buffered. We might need an unbuffered variant as well
+enum seek_style { seek_set, seek_end, seek_cur, }
+
+
+// The raw underlying reader iface. All readers must implement this.
+iface reader {
+    // FIXME: Seekable really should be orthogonal.
+    fn read_bytes(uint) -> [u8];
+    fn read_byte() -> int;
+    fn unread_byte(int);
+    fn eof() -> bool;
+    fn seek(int, seek_style);
+    fn tell() -> uint;
+}
+
+// Generic utility functions defined on readers
+
+impl reader_util for reader {
+    fn read_chars(n: uint) -> [char] {
+        // returns the (consumed offset, n_req), appends characters to &chars
+        fn chars_from_buf(buf: [u8], &chars: [char]) -> (uint, uint) {
+            let mut i = 0u;
+            while i < vec::len(buf) {
+                let b0 = buf[i];
+                let w = str::utf8_char_width(b0);
+                let end = i + w;
+                i += 1u;
+                assert (w > 0u);
+                if w == 1u {
+                    chars += [ b0 as char ];
+                    cont;
+                }
+                // can't satisfy this char with the existing data
+                if end > vec::len(buf) {
+                    ret (i - 1u, end - vec::len(buf));
+                }
+                let mut val = 0u;
+                while i < end {
+                    let next = buf[i] as int;
+                    i += 1u;
+                    assert (next > -1);
+                    assert (next & 192 == 128);
+                    val <<= 6u;
+                    val += (next & 63) as uint;
+                }
+                // See str::char_at
+                val += ((b0 << ((w + 1u) as u8)) as uint)
+                    << (w - 1u) * 6u - w - 1u;
+                chars += [ val as char ];
+            }
+            ret (i, 0u);
+        }
+        let mut buf: [u8] = [];
+        let mut chars: [char] = [];
+        // might need more bytes, but reading n will never over-read
+        let mut nbread = n;
+        while nbread > 0u {
+            let data = self.read_bytes(nbread);
+            if vec::len(data) == 0u {
+                // eof - FIXME should we do something if
+                // we're split in a unicode char?
+                break;
+            }
+            buf += data;
+            let (offset, nbreq) = chars_from_buf(buf, chars);
+            let ncreq = n - vec::len(chars);
+            // again we either know we need a certain number of bytes
+            // to complete a character, or we make sure we don't
+            // over-read by reading 1-byte per char needed
+            nbread = if ncreq > nbreq { ncreq } else { nbreq };
+            if nbread > 0u {
+                buf = vec::slice(buf, offset, vec::len(buf));
+            }
+        }
+        chars
+    }
+
+    fn read_char() -> char {
+        let c = self.read_chars(1u);
+        if vec::len(c) == 0u {
+            ret -1 as char; // FIXME will this stay valid?
+        }
+        assert(vec::len(c) == 1u);
+        ret c[0];
+    }
+
+    fn read_line() -> str {
+        let mut buf: [u8] = [];
+        while true {
+            let ch = self.read_byte();
+            if ch == -1 || ch == 10 { break; }
+            buf += [ch as u8];
+        }
+        str::from_bytes(buf)
+    }
+
+    fn read_c_str() -> str {
+        let mut buf: [u8] = [];
+        while true {
+            let ch = self.read_byte();
+            if ch < 1 { break; } else { buf += [ch as u8]; }
+        }
+        str::from_bytes(buf)
+    }
+
+    // FIXME deal with eof?
+    fn read_le_uint(size: uint) -> uint {
+        let mut val = 0u, pos = 0u, i = size;
+        while i > 0u {
+            val += (self.read_byte() as uint) << pos;
+            pos += 8u;
+            i -= 1u;
+        }
+        val
+    }
+    fn read_le_int(size: uint) -> int {
+        let mut val = 0u, pos = 0u, i = size;
+        while i > 0u {
+            val += (self.read_byte() as uint) << pos;
+            pos += 8u;
+            i -= 1u;
+        }
+        val as int
+    }
+    fn read_be_uint(size: uint) -> uint {
+        let mut val = 0u, i = size;
+        while i > 0u {
+            i -= 1u;
+            val += (self.read_byte() as uint) << i * 8u;
+        }
+        val
+    }
+
+    fn read_whole_stream() -> [u8] {
+        let mut buf: [u8] = [];
+        while !self.eof() { buf += self.read_bytes(2048u); }
+        buf
+    }
+}
+
+// Reader implementations
+
+fn convert_whence(whence: seek_style) -> i32 {
+    ret alt whence {
+      seek_set { 0i32 }
+      seek_cur { 1i32 }
+      seek_end { 2i32 }
+    };
+}
+
+impl of reader for *libc::FILE {
+    fn read_bytes(len: uint) -> [u8] unsafe {
+        let mut buf : [mutable u8] = [mutable];
+        vec::reserve(buf, len);
+        vec::as_mut_buf(buf) {|b|
+            let read = libc::fread(b as *mutable c_void, 1u,
+                                   len, self);
+            vec::unsafe::set_len(buf, read);
+        }
+        ret vec::from_mut(buf);
+    }
+    fn read_byte() -> int { ret libc::fgetc(self) as int; }
+    fn unread_byte(byte: int) { libc::ungetc(byte as c_int, self); }
+    fn eof() -> bool { ret libc::feof(self) != 0 as c_int; }
+    fn seek(offset: int, whence: seek_style) {
+        assert libc::fseek(self, offset, convert_whence(whence))
+            == 0 as c_int;
+    }
+    fn tell() -> uint { ret libc::ftell(self) as uint; }
+}
+
+// A forwarding impl of reader that also holds on to a resource for the
+// duration of its lifetime.
+// FIXME there really should be a better way to do this
+impl <T: reader, C> of reader for {base: T, cleanup: C} {
+    fn read_bytes(len: uint) -> [u8] { self.base.read_bytes(len) }
+    fn read_byte() -> int { self.base.read_byte() }
+    fn unread_byte(byte: int) { self.base.unread_byte(byte); }
+    fn eof() -> bool { self.base.eof() }
+    fn seek(off: int, whence: seek_style) { self.base.seek(off, whence) }
+    fn tell() -> uint { self.base.tell() }
+}
+
+resource FILE_res(f: *libc::FILE) { libc::fclose(f); }
+
+fn FILE_reader(f: *libc::FILE, cleanup: bool) -> reader {
+    if cleanup {
+        {base: f, cleanup: FILE_res(f)} as reader
+    } else {
+        f as reader
+    }
+}
+
+// FIXME: this should either be an iface-less impl, a set of top-level
+// functions that take a reader, or a set of default methods on reader
+// (which can then be called reader)
+
+fn stdin() -> reader { rustrt::rust_get_stdin() as reader }
+
+fn file_reader(path: str) -> result::t<reader, str> {
+    let f = os::as_c_charp(path, {|pathbuf|
+        os::as_c_charp("r", {|modebuf|
+            libc::fopen(pathbuf, modebuf)
+        })
+    });
+    ret if f as uint == 0u { result::err("error opening " + path) }
+    else {
+        result::ok(FILE_reader(f, true))
+    }
+}
+
+
+// Byte buffer readers
+
+// TODO: const u8, but this fails with rustboot.
+type byte_buf = {buf: [u8], mutable pos: uint, len: uint};
+
+impl of reader for byte_buf {
+    fn read_bytes(len: uint) -> [u8] {
+        let rest = self.len - self.pos;
+        let mut to_read = len;
+        if rest < to_read { to_read = rest; }
+        let range = vec::slice(self.buf, self.pos, self.pos + to_read);
+        self.pos += to_read;
+        ret range;
+    }
+    fn read_byte() -> int {
+        if self.pos == self.len { ret -1; }
+        let b = self.buf[self.pos];
+        self.pos += 1u;
+        ret b as int;
+    }
+    fn unread_byte(_byte: int) { #error("TODO: unread_byte"); fail; }
+    fn eof() -> bool { self.pos == self.len }
+    fn seek(offset: int, whence: seek_style) {
+        let pos = self.pos;
+        self.pos = seek_in_buf(offset, pos, self.len, whence);
+    }
+    fn tell() -> uint { self.pos }
+}
+
+fn bytes_reader(bytes: [u8]) -> reader {
+    bytes_reader_between(bytes, 0u, vec::len(bytes))
+}
+
+fn bytes_reader_between(bytes: [u8], start: uint, end: uint) -> reader {
+    {buf: bytes, mutable pos: start, len: end} as reader
+}
+
+fn with_bytes_reader<t>(bytes: [u8], f: fn(reader) -> t) -> t {
+    f(bytes_reader(bytes))
+}
+
+fn with_bytes_reader_between<t>(bytes: [u8], start: uint, end: uint,
+                                f: fn(reader) -> t) -> t {
+    f(bytes_reader_between(bytes, start, end))
+}
+
+fn str_reader(s: str) -> reader {
+    bytes_reader(str::bytes(s))
+}
+
+fn with_str_reader<T>(s: str, f: fn(reader) -> T) -> T {
+    str::as_bytes(s) { |bytes|
+        with_bytes_reader_between(bytes, 0u, str::len(s), f)
+    }
+}
+
+// Writing
+enum fileflag { append, create, truncate, no_flag, }
+
+// FIXME: Seekable really should be orthogonal.
+// FIXME: eventually u64
+iface writer {
+    fn write([const u8]);
+    fn seek(int, seek_style);
+    fn tell() -> uint;
+    fn flush() -> int;
+}
+
+impl <T: writer, C> of writer for {base: T, cleanup: C} {
+    fn write(bs: [const u8]) { self.base.write(bs); }
+    fn seek(off: int, style: seek_style) { self.base.seek(off, style); }
+    fn tell() -> uint { self.base.tell() }
+    fn flush() -> int { self.base.flush() }
+}
+
+impl of writer for *libc::FILE {
+    fn write(v: [const u8]) unsafe {
+        let len = vec::len(v);
+        vec::as_buf(v) {|vbuf|
+            let nout = libc::fwrite(vbuf as *c_void, len, 1u, self);
+            if nout < 1 as size_t {
+                #error("error writing buffer");
+                log(error, sys::last_os_error());
+                fail;
+            }
+        }
+    }
+    fn seek(offset: int, whence: seek_style) {
+        assert libc::fseek(self, offset, convert_whence(whence))
+            == 0 as c_int;
+    }
+    fn tell() -> uint { libc::ftell(self) as uint }
+    fn flush() -> int { libc::fflush(self) as int }
+}
+
+fn FILE_writer(f: *libc::FILE, cleanup: bool) -> writer {
+    if cleanup {
+        {base: f, cleanup: FILE_res(f)} as writer
+    } else {
+        f as writer
+    }
+}
+
+impl of writer for fd_t {
+    fn write(v: [const u8]) unsafe {
+        let len = vec::len(v);
+        let mut count = 0u;
+        vec::as_buf(v) {|vbuf|
+            while count < len {
+                let vb = ptr::offset(vbuf, count) as *c_void;
+                let nout = libc::write(self, vb, len);
+                if nout < 0 as ssize_t {
+                    #error("error writing buffer");
+                    log(error, sys::last_os_error());
+                    fail;
+                }
+                count += nout as uint;
+            }
+        }
+    }
+    fn seek(_offset: int, _whence: seek_style) {
+        #error("need 64-bit native calls for seek, sorry");
+        fail;
+    }
+    fn tell() -> uint {
+        #error("need 64-bit native calls for tell, sorry");
+        fail;
+    }
+    fn flush() -> int { 0 }
+}
+
+resource fd_res(fd: fd_t) { libc::close(fd); }
+
+fn fd_writer(fd: fd_t, cleanup: bool) -> writer {
+    if cleanup {
+        {base: fd, cleanup: fd_res(fd)} as writer
+    } else {
+        fd as writer
+    }
+}
+
+
+fn mk_file_writer(path: str, flags: [fileflag])
+    -> result::t<writer, str> {
+
+    #[cfg(target_os = "win32")]
+    fn wb() -> c_int { (O_WRONLY | O_BINARY) as c_int }
+
+    #[cfg(target_os = "linux")]
+    #[cfg(target_os = "macos")]
+    #[cfg(target_os = "freebsd")]
+    fn wb() -> c_int { O_WRONLY as c_int }
+
+    let mut fflags: c_int = wb();
+    for f: fileflag in flags {
+        alt f {
+          append { fflags |= O_APPEND as c_int; }
+          create { fflags |= O_CREAT as c_int; }
+          truncate { fflags |= O_TRUNC as c_int; }
+          no_flag { }
+        }
+    }
+    let fd = os::as_c_charp(path) {|pathbuf|
+        libc::open(pathbuf, fflags,
+                   (S_IRUSR | S_IWUSR) as c_int)
+    };
+    if fd < (0 as c_int) {
+        // FIXME don't log this! put it in the returned error string
+        log(error, sys::last_os_error());
+        result::err("error opening " + path)
+    } else {
+        result::ok(fd_writer(fd, true))
+    }
+}
+
+fn u64_to_le_bytes(n: u64, size: uint) -> [u8] {
+    let mut bytes: [u8] = [], i = size, n = n;
+    while i > 0u {
+        bytes += [(n & 255_u64) as u8];
+        n >>= 8_u64;
+        i -= 1u;
+    }
+    ret bytes;
+}
+
+fn u64_to_be_bytes(n: u64, size: uint) -> [u8] {
+    assert size <= 8u;
+    let mut bytes: [u8] = [];
+    let mut i = size;
+    while i > 0u {
+        let shift = ((i - 1u) * 8u) as u64;
+        bytes += [(n >> shift) as u8];
+        i -= 1u;
+    }
+    ret bytes;
+}
+
+fn u64_from_be_bytes(data: [u8], start: uint, size: uint) -> u64 {
+    let mut sz = size;
+    assert (sz <= 8u);
+    let mut val = 0_u64;
+    let mut pos = start;
+    while sz > 0u {
+        sz -= 1u;
+        val += (data[pos] as u64) << ((sz * 8u) as u64);
+        pos += 1u;
+    }
+    ret val;
+}
+
+impl writer_util for writer {
+    fn write_char(ch: char) {
+        if ch as uint < 128u {
+            self.write([ch as u8]);
+        } else {
+            self.write(str::bytes(str::from_char(ch)));
+        }
+    }
+    fn write_str(s: str) { self.write(str::bytes(s)); }
+    fn write_line(s: str) { self.write(str::bytes(s + "\n")); }
+    fn write_int(n: int) { self.write(str::bytes(int::to_str(n, 10u))); }
+    fn write_uint(n: uint) { self.write(str::bytes(uint::to_str(n, 10u))); }
+
+    fn write_le_uint(n: uint, size: uint) {
+        self.write(u64_to_le_bytes(n as u64, size));
+    }
+    fn write_le_int(n: int, size: uint) {
+        self.write(u64_to_le_bytes(n as u64, size));
+    }
+
+    fn write_be_uint(n: uint, size: uint) {
+        self.write(u64_to_be_bytes(n as u64, size));
+    }
+    fn write_be_int(n: int, size: uint) {
+        self.write(u64_to_be_bytes(n as u64, size));
+    }
+
+    fn write_be_u64(n: u64) { self.write(u64_to_be_bytes(n, 8u)); }
+    fn write_be_u32(n: u32) { self.write(u64_to_be_bytes(n as u64, 4u)); }
+    fn write_be_u16(n: u16) { self.write(u64_to_be_bytes(n as u64, 2u)); }
+
+    fn write_be_i64(n: i64) { self.write(u64_to_be_bytes(n as u64, 8u)); }
+    fn write_be_i32(n: i32) { self.write(u64_to_be_bytes(n as u64, 4u)); }
+    fn write_be_i16(n: i16) { self.write(u64_to_be_bytes(n as u64, 2u)); }
+
+    fn write_le_u64(n: u64) { self.write(u64_to_le_bytes(n, 8u)); }
+    fn write_le_u32(n: u32) { self.write(u64_to_le_bytes(n as u64, 4u)); }
+    fn write_le_u16(n: u16) { self.write(u64_to_le_bytes(n as u64, 2u)); }
+
+    fn write_le_i64(n: i64) { self.write(u64_to_le_bytes(n as u64, 8u)); }
+    fn write_le_i32(n: i32) { self.write(u64_to_le_bytes(n as u64, 4u)); }
+    fn write_le_i16(n: i16) { self.write(u64_to_le_bytes(n as u64, 2u)); }
+
+    fn write_u8(n: u8) { self.write([n]) }
+}
+
+fn file_writer(path: str, flags: [fileflag]) -> result::t<writer, str> {
+    result::chain(mk_file_writer(path, flags), { |w| result::ok(w)})
+}
+
+
+// FIXME: fileflags
+fn buffered_file_writer(path: str) -> result::t<writer, str> {
+    let f = os::as_c_charp(path) {|pathbuf|
+        os::as_c_charp("w") {|modebuf|
+            libc::fopen(pathbuf, modebuf)
+        }
+    };
+    ret if f as uint == 0u { result::err("error opening " + path) }
+    else { result::ok(FILE_writer(f, true)) }
+}
+
+// FIXME it would be great if this could be a const
+// FIXME why are these different from the way stdin() is implemented?
+fn stdout() -> writer { fd_writer(libc::STDOUT_FILENO as c_int, false) }
+fn stderr() -> writer { fd_writer(libc::STDERR_FILENO as c_int, false) }
+
+fn print(s: str) { stdout().write_str(s); }
+fn println(s: str) { stdout().write_line(s); }
+
+type mem_buffer = @{mutable buf: [mutable u8],
+                    mutable pos: uint};
+
+impl of writer for mem_buffer {
+    fn write(v: [const u8]) {
+        // Fast path.
+        if self.pos == vec::len(self.buf) {
+            for b: u8 in v { self.buf += [mutable b]; }
+            self.pos += vec::len(v);
+            ret;
+        }
+        // FIXME: Optimize: These should be unique pointers.
+        let vlen = vec::len(v);
+        let mut vpos = 0u;
+        while vpos < vlen {
+            let b = v[vpos];
+            if self.pos == vec::len(self.buf) {
+                self.buf += [mutable b];
+            } else { self.buf[self.pos] = b; }
+            self.pos += 1u;
+            vpos += 1u;
+        }
+    }
+    fn seek(offset: int, whence: seek_style) {
+        let pos = self.pos;
+        let len = vec::len(self.buf);
+        self.pos = seek_in_buf(offset, pos, len, whence);
+    }
+    fn tell() -> uint { self.pos }
+    fn flush() -> int { 0 }
+}
+
+fn mk_mem_buffer() -> mem_buffer {
+    @{mutable buf: [mutable], mutable pos: 0u}
+}
+fn mem_buffer_writer(b: mem_buffer) -> writer { b as writer }
+fn mem_buffer_buf(b: mem_buffer) -> [u8] { vec::from_mut(b.buf) }
+fn mem_buffer_str(b: mem_buffer) -> str {
+    let b_ = vec::from_mut(b.buf);
+    str::from_bytes(b_)
+}
+
+fn with_str_writer(f: fn(writer)) -> str {
+    let buf = mk_mem_buffer();
+    let wr = mem_buffer_writer(buf);
+    f(wr);
+    io::mem_buffer_str(buf)
+}
+
+fn with_buf_writer(f: fn(writer)) -> [u8] {
+    let buf = mk_mem_buffer();
+    let wr = mem_buffer_writer(buf);
+    f(wr);
+    io::mem_buffer_buf(buf)
+}
+
+// Utility functions
+fn seek_in_buf(offset: int, pos: uint, len: uint, whence: seek_style) ->
+   uint {
+    let mut bpos = pos as int;
+    let blen = len as int;
+    alt whence {
+      seek_set { bpos = offset; }
+      seek_cur { bpos += offset; }
+      seek_end { bpos = blen + offset; }
+    }
+    if bpos < 0 { bpos = 0; } else if bpos > blen { bpos = blen; }
+    ret bpos as uint;
+}
+
+fn read_whole_file_str(file: str) -> result::t<str, str> {
+    result::chain(read_whole_file(file), { |bytes|
+        result::ok(str::from_bytes(bytes))
+    })
+}
+
+// FIXME implement this in a low-level way. Going through the abstractions is
+// pointless.
+fn read_whole_file(file: str) -> result::t<[u8], str> {
+    result::chain(file_reader(file), { |rdr|
+        result::ok(rdr.read_whole_stream())
+    })
+}
+
+// fsync related
+
+mod fsync {
+
+    enum level {
+        // whatever fsync does on that platform
+        fsync,
+
+        // fdatasync on linux, similiar or more on other platforms
+        fdatasync,
+
+        // full fsync
+        //
+        // You must additionally sync the parent directory as well!
+        fullfsync,
+    }
+
+
+    // Resource of artifacts that need to fsync on destruction
+    resource res<t>(arg: arg<t>) {
+        alt arg.opt_level {
+          option::none { }
+          option::some(level) {
+            // fail hard if not succesful
+            assert(arg.fsync_fn(arg.val, level) != -1);
+          }
+        }
+    }
+
+    type arg<t> = {
+        val: t,
+        opt_level: option<level>,
+        fsync_fn: fn@(t, level) -> int
+    };
+
+    // fsync file after executing blk
+    // FIXME find better way to create resources within lifetime of outer res
+    fn FILE_res_sync(&&file: FILE_res, opt_level: option<level>,
+                  blk: fn(&&res<*libc::FILE>)) {
+        blk(res({
+            val: *file, opt_level: opt_level,
+            fsync_fn: fn@(&&file: *libc::FILE, l: level) -> int {
+                ret os::fsync_fd(libc::fileno(file), l) as int;
+            }
+        }));
+    }
+
+    // fsync fd after executing blk
+    fn fd_res_sync(&&fd: fd_res, opt_level: option<level>,
+                   blk: fn(&&res<fd_t>)) {
+        blk(res({
+            val: *fd, opt_level: opt_level,
+            fsync_fn: fn@(&&fd: fd_t, l: level) -> int {
+                ret os::fsync_fd(fd, l) as int;
+            }
+        }));
+    }
+
+    // Type of objects that may want to fsync
+    iface t { fn fsync(l: level) -> int; }
+
+    // Call o.fsync after executing blk
+    fn obj_sync(&&o: t, opt_level: option<level>, blk: fn(&&res<t>)) {
+        blk(res({
+            val: o, opt_level: opt_level,
+            fsync_fn: fn@(&&o: t, l: level) -> int { ret o.fsync(l); }
+        }));
+    }
+}
+
+#[cfg(test)]
+mod tests {
+
+    #[test]
+    fn test_simple() {
+        let tmpfile: str = "tmp/lib-io-test-simple.tmp";
+        log(debug, tmpfile);
+        let frood: str = "A hoopy frood who really knows where his towel is.";
+        log(debug, frood);
+        {
+            let out: io::writer =
+                result::get(
+                    io::file_writer(tmpfile, [io::create, io::truncate]));
+            out.write_str(frood);
+        }
+        let inp: io::reader = result::get(io::file_reader(tmpfile));
+        let frood2: str = inp.read_c_str();
+        log(debug, frood2);
+        assert (str::eq(frood, frood2));
+    }
+
+    #[test]
+    fn test_readchars_empty() {
+        let inp : io::reader = io::str_reader("");
+        let res : [char] = inp.read_chars(128u);
+        assert(vec::len(res) == 0u);
+    }
+
+    #[test]
+    fn test_readchars_wide() {
+        let wide_test = "生锈的汤匙切肉汤hello生锈的汤匙切肉汤";
+        let ivals : [int] = [
+            29983, 38152, 30340, 27748,
+            21273, 20999, 32905, 27748,
+            104, 101, 108, 108, 111,
+            29983, 38152, 30340, 27748,
+            21273, 20999, 32905, 27748];
+        fn check_read_ln(len : uint, s: str, ivals: [int]) {
+            let inp : io::reader = io::str_reader(s);
+            let res : [char] = inp.read_chars(len);
+            if (len <= vec::len(ivals)) {
+                assert(vec::len(res) == len);
+            }
+            assert(vec::slice(ivals, 0u, vec::len(res)) ==
+                   vec::map(res, {|x| x as int}));
+        }
+        let i = 0u;
+        while i < 8u {
+            check_read_ln(i, wide_test, ivals);
+            i += 1u;
+        }
+        // check a long read for good measure
+        check_read_ln(128u, wide_test, ivals);
+    }
+
+    #[test]
+    fn test_readchar() {
+        let inp : io::reader = io::str_reader("生");
+        let res : char = inp.read_char();
+        assert(res as int == 29983);
+    }
+
+    #[test]
+    fn test_readchar_empty() {
+        let inp : io::reader = io::str_reader("");
+        let res : char = inp.read_char();
+        assert(res as int == -1);
+    }
+
+    #[test]
+    fn file_reader_not_exist() {
+        alt io::file_reader("not a file") {
+          result::err(e) {
+            assert e == "error opening not a file";
+          }
+          result::ok(_) { fail; }
+        }
+    }
+
+    #[test]
+    fn file_writer_bad_name() {
+        alt io::file_writer("?/?", []) {
+          result::err(e) {
+            assert e == "error opening ?/?";
+          }
+          result::ok(_) { fail; }
+        }
+    }
+
+    #[test]
+    fn buffered_file_writer_bad_name() {
+        alt io::buffered_file_writer("?/?") {
+          result::err(e) {
+            assert e == "error opening ?/?";
+          }
+          result::ok(_) { fail; }
+        }
+    }
+}
+
+//
+// Local Variables:
+// mode: rust
+// fill-column: 78;
+// indent-tabs-mode: nil
+// c-basic-offset: 4
+// buffer-file-coding-system: utf-8-unix
+// End:
+//
diff --git a/src/libcore/libc.rs b/src/libcore/libc.rs
index c1ce3c72a30..876e5881871 100644
--- a/src/libcore/libc.rs
+++ b/src/libcore/libc.rs
@@ -96,6 +96,14 @@ export size_t, ptrdiff_t, clock_t, time_t;
 export c_longlong, c_ulonglong, intptr_t, uintptr_t;
 export off_t, dev_t, ino_t, pid_t, mode_t, ssize_t;
 
+export EXIT_FAILURE, EXIT_SUCCESS, RAND_MAX,
+       EOF, SEEK_SET, SEEK_CUR, SEEK_END, _IOFBF, _IONBF, _IOLBF,
+       BUFSIZ, FOPEN_MAX, FILENAME_MAX, L_tmpnam, TMP_MAX,
+       O_RDONLY, O_WRONLY, O_RDWR, O_APPEND, O_CREAT, O_EXCL, O_TRUNC,
+       S_IFIFO, S_IFCHR, S_IFBLK, S_IFDIR, S_IFREG, S_IFMT, S_IEXEC,
+       S_IWRITE, S_IREAD, S_IRWXU, S_IXUSR, S_IWUSR, S_IRUSR, F_OK, R_OK,
+       W_OK, X_OK, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO;
+
 export isalnum, isalpha, iscntrl, isdigit, islower, isprint, ispunct,
        isspace, isupper, isxdigit, tolower, toupper;
 
@@ -111,7 +119,7 @@ export strcpy, strncpy, strcat, strncat, strcmp, strncmp, strcoll, strchr,
        strxfrm, memcpy, memmove, memcmp, memchr, memset;
 
 export chmod, mkdir;
-export popen, pclose, fdopen;
+export popen, pclose, fdopen, fileno;
 export open, creat;
 export access, chdir, close, dup, dup2, execv, execve, execvp, getcwd,
        getpid, isatty, lseek, pipe, read, rmdir, unlink, write;
@@ -495,9 +503,9 @@ mod consts {
             const R_OK : int = 4;
             const W_OK : int = 2;
             const X_OK : int = 1;
-            const STDERR_FILENO : int = 2;
             const STDIN_FILENO : int = 0;
             const STDOUT_FILENO : int = 1;
+            const STDERR_FILENO : int = 2;
         }
         mod posix01 { }
         mod posix08 { }
@@ -558,6 +566,9 @@ mod consts {
             const R_OK : int = 4;
             const W_OK : int = 2;
             const X_OK : int = 1;
+            const STDIN_FILENO : int = 0;
+            const STDOUT_FILENO : int = 1;
+            const STDERR_FILENO : int = 2;
             const F_LOCK : int = 1;
             const F_TEST : int = 3;
             const F_TLOCK : int = 2;
@@ -618,9 +629,9 @@ mod consts {
             const R_OK : int = 4;
             const W_OK : int = 2;
             const X_OK : int = 1;
-            const STDERR_FILENO : int = 2;
             const STDIN_FILENO : int = 0;
             const STDOUT_FILENO : int = 1;
+            const STDERR_FILENO : int = 2;
             const F_LOCK : int = 1;
             const F_TEST : int = 3;
             const F_TLOCK : int = 2;
@@ -682,9 +693,9 @@ mod consts {
             const R_OK : int = 4;
             const W_OK : int = 2;
             const X_OK : int = 1;
-            const STDERR_FILENO : int = 2;
             const STDIN_FILENO : int = 0;
             const STDOUT_FILENO : int = 1;
+            const STDERR_FILENO : int = 2;
             const F_LOCK : int = 1;
             const F_TEST : int = 3;
             const F_TLOCK : int = 2;
@@ -744,7 +755,8 @@ mod funcs {
             fn setbuf(stream: *FILE, buf: *c_char);
             // Omitted: printf and scanf variants.
             fn fgetc(stream: *FILE) -> c_int;
-            fn fgets(buf: *c_char, n: c_int, stream: *FILE) -> *c_char;
+            fn fgets(buf: *mutable c_char, n: c_int,
+                     stream: *FILE) -> *c_char;
             fn fputc(c: c_int, stream: *FILE) -> c_int;
             fn fputs(s: *c_char, stream: *FILE) -> *c_char;
             // Omitted: getc, getchar (might be macros).
@@ -755,7 +767,7 @@ mod funcs {
             // Omitted: putc, putchar (might be macros).
             fn puts(s: *c_char) -> c_int;
             fn ungetc(c: c_int, stream: *FILE) -> c_int;
-            fn fread(ptr: *c_void, size: size_t,
+            fn fread(ptr: *mutable c_void, size: size_t,
                      nobj: size_t, stream: *FILE) -> size_t;
             fn fwrite(ptr: *c_void, size: size_t,
                       nobj: size_t, stream: *FILE) -> size_t;
@@ -863,7 +875,7 @@ mod funcs {
         #[abi = "cdecl"]
         native mod fcntl {
             #[link_name = "_open"]
-            fn open(path: *c_char, oflag: c_int) -> c_int;
+            fn open(path: *c_char, oflag: c_int, mode: c_int) -> c_int;
 
             #[link_name = "_creat"]
             fn creat(path: *c_char, mode: c_int) -> c_int;
@@ -923,7 +935,7 @@ mod funcs {
                     textmode: c_int) -> c_int;
 
             #[link_name = "_read"]
-            fn read(fd: c_int, buf: *c_void, count: c_uint) -> c_int;
+            fn read(fd: c_int, buf: *mutable c_void, count: c_uint) -> c_int;
 
             #[link_name = "_rmdir"]
             fn rmdir(path: *c_char) -> c_int;
@@ -932,7 +944,7 @@ mod funcs {
             fn unlink(c: *c_char) -> c_int;
 
             #[link_name = "_write"]
-            fn write(fd: c_int, buf: *c_void, count: c_uint) -> c_uint;
+            fn write(fd: c_int, buf: *c_void, count: c_uint) -> c_int;
 
         }
     }
@@ -964,7 +976,7 @@ mod funcs {
         #[nolink]
         #[abi = "cdecl"]
         native mod fcntl {
-            fn open(path: *c_char, oflag: c_int) -> c_int;
+            fn open(path: *c_char, oflag: c_int, mode: c_int) -> c_int;
             fn creat(path: *c_char, mode: mode_t) -> c_int;
             fn fcntl(fd: c_int, cmd: c_int) -> c_int;
         }
@@ -999,7 +1011,7 @@ mod funcs {
             fn getegid() -> gid_t;
             fn geteuid() -> uid_t;
             fn getgid() -> gid_t ;
-            fn getgroups(ngroups_max: c_int, groups: *gid_t) -> c_int;
+            fn getgroups(ngroups_max: c_int, groups: *mutable gid_t) -> c_int;
             fn getlogin() -> *c_char;
             fn getopt(argc: c_int, argv: **c_char, optstr: *c_char) -> c_int;
             fn getpgrp() -> pid_t;
@@ -1012,7 +1024,8 @@ mod funcs {
             fn pathconf(path: *c_char, name: c_int) -> c_long;
             fn pause() -> c_int;
             fn pipe(fds: *mutable c_int) -> c_int;
-            fn read(fd: c_int, buf: *c_void, count: size_t) -> ssize_t;
+            fn read(fd: c_int, buf: *mutable c_void,
+                    count: size_t) -> ssize_t;
             fn rmdir(path: *c_char) -> c_int;
             fn setgid(gid: gid_t) -> c_int;
             fn setpgid(pid: pid_t, pgid: pid_t) -> c_int;
diff --git a/src/libcore/os.rs b/src/libcore/os.rs
index 42d74e4a94c..eeca52f7bc0 100644
--- a/src/libcore/os.rs
+++ b/src/libcore/os.rs
@@ -26,11 +26,15 @@ import option = option::t;
 import getcwd = rustrt::rust_getcwd;
 import consts::*;
 
-export close, fclose, fsync_fd;
+export close, fclose, fsync_fd, waitpid;
 export env, getenv, setenv, fdopen, pipe;
 export getcwd, dll_filename, self_exe_path;
 export exe_suffix, dll_suffix, sysname;
-export homedir, list_dir, path_is_dir, path_exists;
+export homedir, list_dir, path_is_dir, path_exists, make_absolute,
+       make_dir, remove_dir, change_dir, remove_file;
+
+// FIXME: move these to str perhaps?
+export as_c_charp, fill_charp_buf;
 
 native mod rustrt {
     fn rust_env_pairs() -> [str];
@@ -76,16 +80,32 @@ mod win32 {
 
     fn fill_utf16_buf_and_decode(f: fn(*mutable u16, dword) -> dword)
         -> option<str> {
-        let buf = vec::to_mut(vec::init_elt(tmpbuf_sz, 0u16));
-        vec::as_mut_buf(buf) {|b|
-            let k : dword = f(b, tmpbuf_sz as dword);
-            if k == (0 as dword) {
-                none
-            } else {
-                let sub = vec::slice(buf, 0u, k as uint);
-                option::some::<str>(str::from_utf16(sub))
+
+        // FIXME: remove these when export globs work properly.
+        import libc::funcs::extra::kernel32::*;
+        import libc::consts::os::extra::*;
+
+        let mut n = tmpbuf_sz;
+        let mut res = none;
+        let mut done = false;
+        while !done {
+            let buf = vec::to_mut(vec::init_elt(n, 0u16));
+            vec::as_mut_buf(buf) {|b|
+                let k : dword = f(b, tmpbuf_sz as dword);
+                if k == (0 as dword) {
+                    done = true;
+                } else if (k == n &&
+                           GetLastError() ==
+                           ERROR_INSUFFICIENT_BUFFER as dword) {
+                    n *= (2 as dword);
+                } else {
+                    let sub = vec::slice(buf, 0u, k as uint);
+                    res = option::some::<str>(str::from_utf16(sub));
+                    done = true;
+                }
             }
         }
+        ret res;
     }
 
     fn as_utf16_p<T>(s: str, f: fn(*u16) -> T) -> T {
@@ -160,41 +180,29 @@ fn fdopen(fd: c_int) -> *FILE {
 
 // fsync related
 
-enum fsync_level {
-    // whatever fsync does on that platform
-    fsync,
-
-    // fdatasync on linux, similiar or more on other platforms
-    fdatasync,
-
-    // full fsync
-    //
-    // You must additionally sync the parent directory as well!
-    fullfsync,
-}
-
 #[cfg(target_os = "win32")]
-fn fsync_fd(fd: c_int, _level: fsync_level) -> c_int {
+fn fsync_fd(fd: c_int, _level: io::fsync::level) -> c_int {
     import libc::funcs::extra::msvcrt::*;
     ret commit(fd);
 }
 
 #[cfg(target_os = "linux")]
-fn fsync_fd(fd: c_int, level: fsync_level) -> c_int {
+fn fsync_fd(fd: c_int, level: io::fsync::level) -> c_int {
     import libc::funcs::posix01::unistd::*;
     alt level {
-      fsync | fullfsync { ret fsync(fd); }
-      fdatasync { ret fdatasync(fd); }
+      io::fsync::fsync
+      | io::fsync::fullfsync { ret fsync(fd); }
+      io::fsync::fdatasync { ret fdatasync(fd); }
     }
 }
 
 #[cfg(target_os = "macos")]
-fn fsync_fd(fd: c_int, level: fsync_level) -> c_int {
+fn fsync_fd(fd: c_int, level: io::fsync::level) -> c_int {
     import libc::consts::os::extra::*;
     import libc::funcs::posix88::fcntl::*;
     import libc::funcs::posix01::unistd::*;
     alt level {
-      fsync { ret fsync(fd); }
+      io::fsync::fsync { ret fsync(fd); }
       _ {
         // According to man fnctl, the ok retval is only specified to be !=-1
         if (fcntl(F_FULLFSYNC as c_int, fd) == -1 as c_int)
@@ -206,7 +214,7 @@ fn fsync_fd(fd: c_int, level: fsync_level) -> c_int {
 }
 
 #[cfg(target_os = "freebsd")]
-fn fsync_fd(fd: c_int, _l: fsync_level) -> c_int {
+fn fsync_fd(fd: c_int, _l: io::fsync::level) -> c_int {
     import libc::funcs::posix01::unistd::*;
     ret fsync(fd);
 }
@@ -577,6 +585,89 @@ mod consts {
 #[cfg(test)]
 mod tests {
 
+
+    fn make_rand_name() -> str {
+        import rand;
+        let rng: rand::rng = rand::mk_rng();
+        let n = "TEST" + rng.gen_str(10u);
+        assert option::is_none(getenv(n));
+        n
+    }
+
+    #[test]
+    #[ignore(reason = "fails periodically on mac")]
+    fn test_setenv() {
+        let n = make_rand_name();
+        setenv(n, "VALUE");
+        assert getenv(n) == option::some("VALUE");
+    }
+
+    #[test]
+    #[ignore(reason = "fails periodically on mac")]
+    fn test_setenv_overwrite() {
+        let n = make_rand_name();
+        setenv(n, "1");
+        setenv(n, "2");
+        assert getenv(n) == option::some("2");
+        setenv(n, "");
+        assert getenv(n) == option::some("");
+    }
+
+    // Windows GetEnvironmentVariable requires some extra work to make sure
+    // the buffer the variable is copied into is the right size
+    #[test]
+    #[ignore(reason = "fails periodically on mac")]
+    fn test_getenv_big() {
+        let s = "";
+        let i = 0;
+        while i < 100 { s += "aaaaaaaaaa"; i += 1; }
+        let n = make_rand_name();
+        setenv(n, s);
+        log(debug, s);
+        assert getenv(n) == option::some(s);
+    }
+
+    #[test]
+    fn test_self_exe_path() {
+        let path = os::self_exe_path();
+        assert option::is_some(path);
+        let path = option::get(path);
+        log(debug, path);
+
+        // Hard to test this function
+        if os::sysname() != "win32" {
+            assert str::starts_with(path, path::path_sep());
+        } else {
+            assert path[1] == ':' as u8;
+        }
+    }
+
+    #[test]
+    fn test_env_getenv() {
+        let e = env();
+        assert vec::len(e) > 0u;
+        for (n, v) in e {
+            log(debug, n);
+            let v2 = getenv(n);
+            // MingW seems to set some funky environment variables like
+            // "=C:=C:\MinGW\msys\1.0\bin" and "!::=::\" that are returned
+            // from env() but not visible from getenv().
+            assert option::is_none(v2) || v2 == option::some(v);
+        }
+    }
+
+    #[test]
+    fn test_env_setenv() {
+        let n = make_rand_name();
+
+        let e = env();
+        setenv(n, "VALUE");
+        assert !vec::contains(e, (n, "VALUE"));
+
+        e = env();
+        assert vec::contains(e, (n, "VALUE"));
+    }
+
     #[test]
     fn test() {
         assert (!path::path_is_absolute("test-path"));
diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs
index 8206cdd45f7..72552d318d2 100644
--- a/src/libcore/ptr.rs
+++ b/src/libcore/ptr.rs
@@ -12,9 +12,9 @@ export memmove;
 #[abi = "rust-intrinsic"]
 native mod rusti {
     fn addr_of<T>(val: T) -> *T;
-    fn ptr_offset<T>(ptr: *T, count: ctypes::uintptr_t) -> *T;
-    fn memcpy<T>(dst: *T, src: *T, count: ctypes::uintptr_t);
-    fn memmove<T>(dst: *T, src: *T, count: ctypes::uintptr_t);
+    fn ptr_offset<T>(ptr: *T, count: libc::uintptr_t) -> *T;
+    fn memcpy<T>(dst: *T, src: *T, count: libc::uintptr_t);
+    fn memmove<T>(dst: *T, src: *T, count: libc::uintptr_t);
 }
 
 #[doc = "Get an unsafe pointer to a value"]
diff --git a/src/libcore/rand.rs b/src/libcore/rand.rs
new file mode 100644
index 00000000000..5744323fe77
--- /dev/null
+++ b/src/libcore/rand.rs
@@ -0,0 +1,113 @@
+#[doc = "Random number generation"]
+
+enum rctx {}
+
+#[abi = "cdecl"]
+native mod rustrt {
+    fn rand_new() -> *rctx;
+    fn rand_next(c: *rctx) -> u32;
+    fn rand_free(c: *rctx);
+}
+
+#[doc = "A random number generator"]
+iface rng {
+    #[doc = "Return the next random integer"]
+    fn next() -> u32;
+
+    #[doc = "Return the next random float"]
+    fn next_float() -> float;
+
+    #[doc = "Return a random string composed of A-Z, a-z, 0-9."]
+    fn gen_str(len: uint) -> str;
+
+    #[doc = "Return a random byte string."]
+    fn gen_bytes(len: uint) -> [u8];
+}
+
+resource rand_res(c: *rctx) { rustrt::rand_free(c); }
+
+#[doc = "Create a random number generator"]
+fn mk_rng() -> rng {
+    impl of rng for @rand_res {
+        fn next() -> u32 { ret rustrt::rand_next(**self); }
+        fn next_float() -> float {
+          let u1 = rustrt::rand_next(**self) as float;
+          let u2 = rustrt::rand_next(**self) as float;
+          let u3 = rustrt::rand_next(**self) as float;
+          let scale = u32::max_value as float;
+          ret ((u1 / scale + u2) / scale + u3) / scale;
+        }
+        fn gen_str(len: uint) -> str {
+            let charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
+                          "abcdefghijklmnopqrstuvwxyz" +
+                          "0123456789";
+            let mut s = "";
+            let mut i = 0u;
+            while (i < len) {
+                let n = rustrt::rand_next(**self) as uint %
+                    str::len(charset);
+                s = s + str::from_char(str::char_at(charset, n));
+                i += 1u;
+            }
+            s
+        }
+        fn gen_bytes(len: uint) -> [u8] {
+            let mut v = [];
+            let mut i = 0u;
+            while i < len {
+                let n = rustrt::rand_next(**self) as uint;
+                v += [(n % (u8::max_value as uint)) as u8];
+                i += 1u;
+            }
+            v
+        }
+    }
+    @rand_res(rustrt::rand_new()) as rng
+}
+
+#[cfg(test)]
+mod tests {
+
+    #[test]
+    fn test() {
+        let r1: rand::rng = rand::mk_rng();
+        log(debug, r1.next());
+        log(debug, r1.next());
+        {
+            let r2 = rand::mk_rng();
+            log(debug, r1.next());
+            log(debug, r2.next());
+            log(debug, r1.next());
+            log(debug, r1.next());
+            log(debug, r2.next());
+            log(debug, r2.next());
+            log(debug, r1.next());
+            log(debug, r1.next());
+            log(debug, r1.next());
+            log(debug, r2.next());
+            log(debug, r2.next());
+            log(debug, r2.next());
+        }
+        log(debug, r1.next());
+        log(debug, r1.next());
+    }
+
+    #[test]
+    fn genstr() {
+        let r: rand::rng = rand::mk_rng();
+        log(debug, r.gen_str(10u));
+        log(debug, r.gen_str(10u));
+        log(debug, r.gen_str(10u));
+        assert(str::len(r.gen_str(10u)) == 10u);
+        assert(str::len(r.gen_str(16u)) == 16u);
+    }
+}
+
+
+// Local Variables:
+// mode: rust;
+// fill-column: 78;
+// indent-tabs-mode: nil
+// c-basic-offset: 4
+// buffer-file-coding-system: utf-8-unix
+// End:
diff --git a/src/libcore/run.rs b/src/libcore/run.rs
new file mode 100644
index 00000000000..4777b74c00e
--- /dev/null
+++ b/src/libcore/run.rs
@@ -0,0 +1,394 @@
+#[doc ="Process spawning"];
+import option::{some, none};
+import str::sbuf;
+import libc::{pid_t, c_void, c_int};
+
+export program;
+export run_program;
+export start_program;
+export program_output;
+export spawn_process;
+export waitpid;
+
+#[abi = "cdecl"]
+native mod rustrt {
+    fn rust_run_program(argv: *sbuf, envp: *c_void, dir: sbuf,
+                        in_fd: c_int, out_fd: c_int, err_fd: c_int)
+        -> pid_t;
+}
+
+#[doc ="A value representing a child process"]
+iface program {
+    #[doc ="Returns the process id of the program"]
+    fn get_id() -> pid_t;
+
+    #[doc ="Returns an io::writer that can be used to write to stdin"]
+    fn input() -> io::writer;
+
+    #[doc ="Returns an io::reader that can be used to read from stdout"]
+    fn output() -> io::reader;
+
+    #[doc ="Returns an io::reader that can be used to read from stderr"]
+    fn err() -> io::reader;
+
+    #[doc = "Closes the handle to the child processes standard input"]
+    fn close_input();
+
+    #[doc = "
+    Waits for the child process to terminate. Closes the handle
+    to stdin if necessary.
+    "]
+    fn finish() -> int;
+
+    #[doc ="Closes open handles"]
+    fn destroy();
+}
+
+
+#[doc = "
+Run a program, providing stdin, stdout and stderr handles
+
+# Arguments
+
+* prog - The path to an executable
+* args - Vector of arguments to pass to the child process
+* env - optional env-modification for child
+* dir - optional dir to run child in (default current dir)
+* in_fd - A file descriptor for the child to use as std input
+* out_fd - A file descriptor for the child to use as std output
+* err_fd - A file descriptor for the child to use as std error
+
+# Return value
+
+The process id of the spawned process
+"]
+fn spawn_process(prog: str, args: [str],
+                 env: option<[(str,str)]>,
+                 dir: option<str>,
+                 in_fd: c_int, out_fd: c_int, err_fd: c_int)
+   -> pid_t unsafe {
+    with_argv(prog, args) {|argv|
+        with_envp(env) { |envp|
+            with_dirp(dir) { |dirp|
+                rustrt::rust_run_program(argv, envp, dirp,
+                                         in_fd, out_fd, err_fd)
+            }
+        }
+    }
+}
+
+fn with_argv<T>(prog: str, args: [str],
+                cb: fn(*sbuf) -> T) -> T unsafe {
+    let mut argptrs = str::as_buf(prog) {|b| [b] };
+    let mut tmps = [];
+    for arg in args {
+        let t = @arg;
+        tmps += [t];
+        argptrs += str::as_buf(*t) {|b| [b] };
+    }
+    argptrs += [ptr::null()];
+    vec::as_buf(argptrs, cb)
+}
+
+#[cfg(target_os = "macos")]
+#[cfg(target_os = "linux")]
+#[cfg(target_os = "freebsd")]
+fn with_envp<T>(env: option<[(str,str)]>,
+                cb: fn(*c_void) -> T) -> T unsafe {
+    // On posixy systems we can pass a char** for envp, which is
+    // a null-terminated array of "k=v\n" strings.
+    alt env {
+      some (es) {
+        let mut tmps = [];
+        let mut ptrs = [];
+
+        for (k,v) in es {
+            let t = @(#fmt("%s=%s", k, v));
+            vec::push(tmps, t);
+            ptrs += str::as_buf(*t) {|b| [b]};
+        }
+        ptrs += [ptr::null()];
+        vec::as_buf(ptrs) { |p| cb(::unsafe::reinterpret_cast(p)) }
+      }
+      none {
+        cb(ptr::null())
+      }
+    }
+}
+
+#[cfg(target_os = "win32")]
+fn with_envp<T>(env: option<[(str,str)]>,
+                cb: fn(*c_void) -> T) -> T unsafe {
+    // On win32 we pass an "environment block" which is not a char**, but
+    // rather a concatenation of null-terminated k=v\0 sequences, with a final
+    // \0 to terminate.
+    alt env {
+      some (es) {
+        let mut blk : [u8] = [];
+        for (k,v) in es {
+            let t = #fmt("%s=%s", k, v);
+            let mut v : [u8] = ::unsafe::reinterpret_cast(t);
+            blk += v;
+            ::unsafe::leak(v);
+        }
+        blk += [0_u8];
+        vec::as_buf(blk) {|p| cb(::unsafe::reinterpret_cast(p)) }
+      }
+      none {
+        cb(ptr::null())
+      }
+    }
+}
+
+fn with_dirp<T>(d: option<str>,
+                cb: fn(sbuf) -> T) -> T unsafe {
+    alt d {
+      some(dir) { str::as_buf(dir, cb) }
+      none { cb(ptr::null()) }
+    }
+}
+
+#[doc ="
+Spawns a process and waits for it to terminate
+
+# Arguments
+
+* prog - The path to an executable
+* args - Vector of arguments to pass to the child process
+
+# Return value
+
+The process id
+"]
+fn run_program(prog: str, args: [str]) -> int {
+    ret waitpid(spawn_process(prog, args, none, none,
+                              0i32, 0i32, 0i32));
+}
+
+#[doc ="
+Spawns a process and returns a program
+
+The returned value is a boxed resource containing a <program> object that can
+be used for sending and recieving data over the standard file descriptors.
+The resource will ensure that file descriptors are closed properly.
+
+# Arguments
+
+* prog - The path to an executable
+* args - Vector of arguments to pass to the child process
+
+# Return value
+
+A boxed resource of <program>
+"]
+fn start_program(prog: str, args: [str]) -> program {
+    let pipe_input = os::pipe();
+    let pipe_output = os::pipe();
+    let pipe_err = os::pipe();
+    let pid =
+        spawn_process(prog, args, none, none,
+                      pipe_input.in, pipe_output.out,
+                      pipe_err.out);
+
+    if pid == -1i32 { fail; }
+    libc::close(pipe_input.in);
+    libc::close(pipe_output.out);
+    libc::close(pipe_err.out);
+
+    type prog_repr = {pid: pid_t,
+                      mutable in_fd: c_int,
+                      out_file: *libc::FILE,
+                      err_file: *libc::FILE,
+                      mutable finished: bool};
+
+    fn close_repr_input(r: prog_repr) {
+        let invalid_fd = -1i32;
+        if r.in_fd != invalid_fd {
+            libc::close(r.in_fd);
+            r.in_fd = invalid_fd;
+        }
+    }
+    fn finish_repr(r: prog_repr) -> int {
+        if r.finished { ret 0; }
+        r.finished = true;
+        close_repr_input(r);
+        ret waitpid(r.pid);
+    }
+    fn destroy_repr(r: prog_repr) {
+        finish_repr(r);
+       libc::fclose(r.out_file);
+       libc::fclose(r.err_file);
+    }
+    resource prog_res(r: prog_repr) { destroy_repr(r); }
+
+    impl of program for prog_res {
+        fn get_id() -> pid_t { ret self.pid; }
+        fn input() -> io::writer { io::fd_writer(self.in_fd, false) }
+        fn output() -> io::reader { io::FILE_reader(self.out_file, false) }
+        fn err() -> io::reader { io::FILE_reader(self.err_file, false) }
+        fn close_input() { close_repr_input(*self); }
+        fn finish() -> int { finish_repr(*self) }
+        fn destroy() { destroy_repr(*self); }
+    }
+    let repr = {pid: pid,
+                mutable in_fd: pipe_input.out,
+                out_file: os::fdopen(pipe_output.in),
+                err_file: os::fdopen(pipe_err.in),
+                mutable finished: false};
+    ret prog_res(repr) as program;
+}
+
+fn read_all(rd: io::reader) -> str {
+    let mut buf = "";
+    while !rd.eof() {
+        let bytes = rd.read_bytes(4096u);
+        buf += str::from_bytes(bytes);
+    }
+    ret buf;
+}
+
+#[doc ="
+Spawns a process, waits for it to exit, and returns the exit code, and
+contents of stdout and stderr.
+
+# Arguments
+
+* prog - The path to an executable
+* args - Vector of arguments to pass to the child process
+
+# Return value
+
+A record, {status: int, out: str, err: str} containing the exit code,
+the contents of stdout and the contents of stderr.
+"]
+fn program_output(prog: str, args: [str]) ->
+   {status: int, out: str, err: str} {
+    let pr = start_program(prog, args);
+    pr.close_input();
+    let out = read_all(pr.output());
+    let err = read_all(pr.err());
+    ret {status: pr.finish(), out: out, err: err};
+}
+
+#[doc ="Waits for a process to exit and returns the exit code"]
+fn waitpid(pid: pid_t) -> int {
+    ret waitpid_os(pid);
+
+    #[cfg(target_os = "win32")]
+    fn waitpid_os(pid: pid_t) -> int {
+        os::waitpid(pid) as int
+    }
+
+    #[cfg(target_os = "linux")]
+    #[cfg(target_os = "macos")]
+    #[cfg(target_os = "freebsd")]
+    fn waitpid_os(pid: pid_t) -> int {
+        #[cfg(target_os = "linux")]
+        fn WIFEXITED(status: i32) -> bool {
+            (status & 0xffi32) == 0i32
+        }
+
+        #[cfg(target_os = "macos")]
+        #[cfg(target_os = "freebsd")]
+        fn WIFEXITED(status: i32) -> bool {
+            (status & 0x7fi32) == 0i32
+        }
+
+        #[cfg(target_os = "linux")]
+        fn WEXITSTATUS(status: i32) -> i32 {
+            (status >> 8i32) & 0xffi32
+        }
+
+        #[cfg(target_os = "macos")]
+        #[cfg(target_os = "freebsd")]
+        fn WEXITSTATUS(status: i32) -> i32 {
+            status >> 8i32
+        }
+
+        let status = os::waitpid(pid);
+        ret if WIFEXITED(status) {
+            WEXITSTATUS(status) as int
+        } else {
+            1
+        };
+    }
+}
+
+#[cfg(test)]
+mod tests {
+
+    import io::writer_util;
+
+    // Regression test for memory leaks
+    #[ignore(cfg(target_os = "win32"))] // FIXME
+    fn test_leaks() {
+        run::run_program("echo", []);
+        run::start_program("echo", []);
+        run::program_output("echo", []);
+    }
+
+    #[test]
+    fn test_pipes() {
+        let pipe_in = os::pipe();
+        let pipe_out = os::pipe();
+        let pipe_err = os::pipe();
+
+        let pid =
+            run::spawn_process(
+                "cat", [], none, none,
+                pipe_in.in, pipe_out.out, pipe_err.out);
+        os::close(pipe_in.in);
+        os::close(pipe_out.out);
+        os::close(pipe_err.out);
+
+        if pid == -1i32 { fail; }
+        let expected = "test";
+        writeclose(pipe_in.out, expected);
+        let actual = readclose(pipe_out.in);
+        readclose(pipe_err.in);
+        os::waitpid(pid);
+
+        log(debug, expected);
+        log(debug, actual);
+        assert (expected == actual);
+
+        fn writeclose(fd: c_int, s: str) {
+            #error("writeclose %d, %s", fd as int, s);
+            let writer = io::fd_writer(fd, false);
+            writer.write_str(s);
+
+            os::close(fd);
+        }
+
+        fn readclose(fd: c_int) -> str {
+            // Copied from run::program_output
+            let file = os::fdopen(fd);
+            let reader = io::FILE_reader(file, false);
+            let buf = "";
+            while !reader.eof() {
+                let bytes = reader.read_bytes(4096u);
+                buf += str::from_bytes(bytes);
+            }
+            os::fclose(file);
+            ret buf;
+        }
+    }
+
+    #[test]
+    fn waitpid() {
+        let pid = run::spawn_process("false", [],
+                                     none, none,
+                                     0i32, 0i32, 0i32);
+        let status = run::waitpid(pid);
+        assert status == 1;
+    }
+
+}
+
+// Local Variables:
+// mode: rust
+// fill-column: 78;
+// indent-tabs-mode: nil
+// c-basic-offset: 4
+// buffer-file-coding-system: utf-8-unix
+// End:
diff --git a/src/libcore/str.rs b/src/libcore/str.rs
index 52f342c81d5..b057d8d1483 100644
--- a/src/libcore/str.rs
+++ b/src/libcore/str.rs
@@ -99,7 +99,7 @@ export
 #[abi = "cdecl"]
 native mod rustrt {
     fn rust_str_push(&s: str, ch: u8);
-    fn str_reserve_shared(&ss: str, nn: ctypes::size_t);
+    fn str_reserve_shared(&ss: str, nn: libc::size_t);
 }
 
 // FIXME: add pure to a lot of functions
diff --git a/src/libcore/sys.rs b/src/libcore/sys.rs
index fc166df7fd4..b402f98ed26 100644
--- a/src/libcore/sys.rs
+++ b/src/libcore/sys.rs
@@ -10,9 +10,9 @@ export log_str;
 export set_exit_status;
 
 enum type_desc = {
-    first_param: **ctypes::c_int,
-    size: ctypes::size_t,
-    align: ctypes::size_t
+    first_param: **libc::c_int,
+    size: libc::size_t,
+    align: libc::size_t
     // Remaining fields not listed
 };
 
@@ -22,10 +22,10 @@ native mod rustrt {
     // available outside this crate. Otherwise it's
     // visible-in-crate, but not re-exported.
     fn last_os_error() -> str;
-    fn refcount<T>(t: @T) -> ctypes::intptr_t;
+    fn refcount<T>(t: @T) -> libc::intptr_t;
     fn unsupervise();
     fn shape_log_str<T>(t: *sys::type_desc, data: T) -> str;
-    fn rust_set_exit_status(code: ctypes::intptr_t);
+    fn rust_set_exit_status(code: libc::intptr_t);
 }
 
 #[abi = "rust-intrinsic"]
@@ -34,7 +34,7 @@ native mod rusti {
 
     // Invokes __builtin_frame_address().
     // See <http://gcc.gnu.org/onlinedocs/gcc/Return-Address.html>.
-    fn frame_address(n: ctypes::c_uint) -> ctypes::uintptr_t;
+    fn frame_address(n: libc::c_uint) -> libc::uintptr_t;
 }
 
 #[doc = "
@@ -64,7 +64,7 @@ fn align_of<T>() -> uint unsafe {
 
 #[doc = "Returns the refcount of a shared box"]
 fn refcount<T>(t: @T) -> uint {
-    ret rustrt::refcount::<T>(t);
+    ret rustrt::refcount::<T>(t) as uint;
 }
 
 fn log_str<T>(t: T) -> str {
@@ -80,7 +80,7 @@ supervised by the scheduler then any user-specified exit status is ignored and
 the process exits with the default failure status
 "]
 fn set_exit_status(code: int) {
-    rustrt::rust_set_exit_status(code as ctypes::intptr_t);
+    rustrt::rust_set_exit_status(code as libc::intptr_t);
 }
 
 #[cfg(test)]
diff --git a/src/libcore/task.rs b/src/libcore/task.rs
index 322b43e223d..580636043ed 100644
--- a/src/libcore/task.rs
+++ b/src/libcore/task.rs
@@ -465,8 +465,8 @@ type task_id = int;
 
 // These are both opaque runtime/compiler types that we don't know the
 // structure of and should only deal with via unsafe pointer
-type rust_task = ctypes::void;
-type rust_closure = ctypes::void;
+type rust_task = libc::c_void;
+type rust_closure = libc::c_void;
 
 fn spawn_raw(opts: task_opts, +f: fn~()) unsafe {
 
@@ -537,7 +537,7 @@ native mod rusti {
 
 native mod rustrt {
     fn rust_get_sched_id() -> sched_id;
-    fn rust_new_sched(num_threads: ctypes::uintptr_t) -> sched_id;
+    fn rust_new_sched(num_threads: libc::uintptr_t) -> sched_id;
 
     fn get_task_id() -> task_id;
     fn rust_get_task() -> *rust_task;
@@ -749,12 +749,12 @@ fn test_spawn_sched_childs_on_same_sched() {
 #[nolink]
 #[cfg(test)]
 native mod testrt {
-    fn rust_dbg_lock_create() -> *ctypes::void;
-    fn rust_dbg_lock_destroy(lock: *ctypes::void);
-    fn rust_dbg_lock_lock(lock: *ctypes::void);
-    fn rust_dbg_lock_unlock(lock: *ctypes::void);
-    fn rust_dbg_lock_wait(lock: *ctypes::void);
-    fn rust_dbg_lock_signal(lock: *ctypes::void);
+    fn rust_dbg_lock_create() -> *libc::c_void;
+    fn rust_dbg_lock_destroy(lock: *libc::c_void);
+    fn rust_dbg_lock_lock(lock: *libc::c_void);
+    fn rust_dbg_lock_unlock(lock: *libc::c_void);
+    fn rust_dbg_lock_wait(lock: *libc::c_void);
+    fn rust_dbg_lock_signal(lock: *libc::c_void);
 }
 
 #[test]
diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs
index 2b3829dd910..cf2d208955b 100644
--- a/src/libcore/vec.rs
+++ b/src/libcore/vec.rs
@@ -73,17 +73,17 @@ export u8;
 
 #[abi = "rust-intrinsic"]
 native mod rusti {
-    fn vec_len<T>(&&v: [const T]) -> ctypes::size_t;
+    fn vec_len<T>(&&v: [const T]) -> libc::size_t;
 }
 
 #[abi = "cdecl"]
 native mod rustrt {
     fn vec_reserve_shared<T>(t: *sys::type_desc,
                              &v: [const T],
-                             n: ctypes::size_t);
+                             n: libc::size_t);
     fn vec_from_buf_shared<T>(t: *sys::type_desc,
                               ptr: *T,
-                              count: ctypes::size_t) -> [T];
+                              count: libc::size_t) -> [T];
 }
 
 #[doc = "A function used to initialize the elements of a vector"]
@@ -912,7 +912,7 @@ mod u8 {
     pure fn cmp(&&a: [u8], &&b: [u8]) -> int unsafe {
         let a_len = len(a);
         let b_len = len(b);
-        let n = uint::min(a_len, b_len) as ctypes::size_t;
+        let n = uint::min(a_len, b_len) as libc::size_t;
         let r = libc::memcmp(unsafe::to_ptr(a) as *libc::c_void,
                              unsafe::to_ptr(b) as *libc::c_void, n) as int;