about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/etc/libc.c3
-rw-r--r--src/libcore/core.rc6
-rw-r--r--src/libcore/core.rs6
-rw-r--r--src/libcore/libc.rs228
-rw-r--r--src/libcore/os.rs529
-rw-r--r--src/libcore/path.rs505
-rw-r--r--src/libcore/str.rs2
-rw-r--r--src/libcore/vec.rs4
8 files changed, 1270 insertions, 13 deletions
diff --git a/src/etc/libc.c b/src/etc/libc.c
index 5ab0c74f180..a91c4452088 100644
--- a/src/etc/libc.c
+++ b/src/etc/libc.c
@@ -10,6 +10,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <limits.h>
+#include <wchar.h>
 
 /* c99 */
 #include <inttypes.h>
@@ -53,6 +54,8 @@ void c95_types() {
   put_type("clock_t", clock_t);
   put_type("time_t", time_t);
 
+  put_type("wchar_t", wchar_t);
+
   printf("    }\n");
 }
 
diff --git a/src/libcore/core.rc b/src/libcore/core.rc
index 37e7fe6ec02..ee3f90a3333 100644
--- a/src/libcore/core.rc
+++ b/src/libcore/core.rc
@@ -82,6 +82,12 @@ mod to_str;
 // Runtime and language-primitive support
 
 mod libc;
+
+// Rename when transition done.
+#[path = "os.rs"]
+mod new_os;
+mod path;
+
 mod ctypes;
 mod math;
 mod cmath;
diff --git a/src/libcore/core.rs b/src/libcore/core.rs
index c68e4ddf617..96bff1250fc 100644
--- a/src/libcore/core.rs
+++ b/src/libcore/core.rs
@@ -1,12 +1,12 @@
 // Top-level, visible-everywhere definitions.
 
-// Export type option as a synonym for option and export the some and none
-// enum constructors.
+// Export various ubiquitous types, constructors, methods.
 
 import option::{some, none};
 import option = option::t;
+import path = path::path;
 import vec::vec_len;
-export option, some, none, vec_len;
+export path, option, some, none, vec_len;
 
 // Export the log levels as global constants. Higher levels mean
 // more-verbosity. Error is the bottom level, default logging level is
diff --git a/src/libcore/libc.rs b/src/libcore/libc.rs
index 60ede88c89a..0c16a378c25 100644
--- a/src/libcore/libc.rs
+++ b/src/libcore/libc.rs
@@ -5,7 +5,8 @@
 //   - ISO 9899:1990 ('C95', 'ANSI C', 'Standard C'), NA1, 1995.
 //   - ISO 9899:1999 ('C99' or 'C9x').
 //   - ISO 9945:1988 / IEEE 1003.1-1988 ('POSIX.1').
-//   - ISO 9945:2008 / IEEE 1003.1-2008 ('POSIX:2008').
+//   - ISO 9945:2001 / IEEE 1003.1-2001 ('POSIX:2001', 'SUSv3').
+//   - ISO 9945:2008 / IEEE 1003.1-2008 ('POSIX:2008', 'SUSv4').
 //
 // Despite having several names each, these are *reasonably* coherent
 // point-in-time, list-of-definition sorts of specs. You can get each under a
@@ -17,9 +18,18 @@
 // platforms.
 //
 // We therefore wind up dividing our module-space up (mostly for the sake of
-// sanity while editing) into definitions common-to-all (held in modules named
-// c95, c99, posix88, and posix08) and definitions that appear only on *some*
-// platforms (named 'extra').
+// sanity while editing, filling-in-details and eliminating duplication) into
+// definitions common-to-all (held in modules named c95, c99, posix88, posix01
+// and posix08) and definitions that appear only on *some* platforms (named
+// 'extra'). This would be things like significant OSX foundation kit, or
+// win32 library kernel32.dll, or various fancy glibc, linux or BSD
+// extensions.
+//
+// In addition to the per-platform 'extra' modules, we define a module of
+// "common BSD" libc routines that never quite made it into POSIX but show up
+// in multiple derived systems. This is the 4.4BSD r2 / 1995 release, the
+// final one from Berkeley after the lawsuits died down and the CSRG
+// dissolved.
 //
 
 // Initial glob-exports mean that all the contents of all the modules
@@ -28,14 +38,25 @@
 // FIXME: change these to glob-exports when sufficiently supported.
 
 import types::common::c95::*;
+import types::common::c99::*;
 import types::common::posix88::*;
+import types::common::posix01::*;
+import types::common::posix08::*;
+import types::common::bsd44::*;
 import types::os::arch::c95::*;
 import types::os::arch::c99::*;
 import types::os::arch::posix88::*;
+import types::os::arch::posix01::*;
+import types::os::arch::posix08::*;
+import types::os::arch::bsd44::*;
 import types::os::arch::extra::*;
 
 import consts::os::c95::*;
+import consts::os::c99::*;
 import consts::os::posix88::*;
+import consts::os::posix01::*;
+import consts::os::posix08::*;
+import consts::os::bsd44::*;
 import consts::os::extra::*;
 
 import funcs::c95::ctype::*;
@@ -49,6 +70,18 @@ import funcs::posix88::fcntl::*;
 import funcs::posix88::dirent::*;
 import funcs::posix88::unistd::*;
 
+import funcs::posix01::unistd::*;
+import funcs::posix08::unistd::*;
+
+import funcs::bsd44::*;
+import funcs::extra::*;
+
+// FIXME: remove these 3 exports (and their uses next door in os::) when
+// export globs work. They provide access (for now) for os:: to dig around in
+// the rest of the platform-specific definitions.
+
+export types, funcs, consts;
+
 // Explicit export lists for the intersection (provided here) mean that
 // you can write more-platform-agnostic code if you stick to just these
 // symbols.
@@ -76,7 +109,7 @@ export strcpy, strncpy, strcat, strncat, strcmp, strncmp, strcoll, strchr,
        strxfrm, memcpy, memmove, memcmp, memchr, memset;
 
 export chmod, mkdir;
-export popen, pclose;
+export popen, pclose, fdopen;
 export open, creat;
 export access, chdir, close, dup, dup2, execv, execve, execvp, getcwd,
        getpid, isatty, lseek, pipe, read, rmdir, unlink, write;
@@ -95,11 +128,14 @@ mod types {
             enum FILE {}
             enum fpos_t {}
         }
-
+        mod c99 { }
         mod posix88 {
             enum DIR {}
             enum dirent {}
         }
+        mod posix01 { }
+        mod posix08 { }
+        mod bsd44 { }
     }
 
     // Standard types that are scalar but vary by OS and arch.
@@ -122,6 +158,7 @@ mod types {
                 type ptrdiff_t = i32;
                 type clock_t = i32;
                 type time_t = i32;
+                type wchar_t = i32;
             }
             mod c99 {
                 type c_longlong = i64;
@@ -140,6 +177,9 @@ mod types {
                 type mode_t = u32;
                 type ssize_t = i32;
             }
+            mod posix01 { }
+            mod posix08 { }
+            mod bsd44 { }
             mod extra {
             }
         }
@@ -160,6 +200,7 @@ mod types {
                 type ptrdiff_t = i64;
                 type clock_t = i64;
                 type time_t = i64;
+                type wchar_t = i32;
             }
             mod c99 {
                 type c_longlong = i64;
@@ -178,6 +219,9 @@ mod types {
                 type mode_t = u32;
                 type ssize_t = i64;
             }
+            mod posix01 { }
+            mod posix08 { }
+            mod bsd44 { }
             mod extra {
             }
         }
@@ -201,6 +245,7 @@ mod types {
                 type ptrdiff_t = i64;
                 type clock_t = i32;
                 type time_t = i64;
+                type wchar_t = i32;
             }
             mod c99 {
                 type c_longlong = i64;
@@ -219,6 +264,9 @@ mod types {
                 type mode_t = u16;
                 type ssize_t = i64;
             }
+            mod posix01 { }
+            mod posix08 { }
+            mod bsd44 { }
             mod extra {
             }
         }
@@ -242,6 +290,7 @@ mod types {
                 type ptrdiff_t = i32;
                 type clock_t = i32;
                 type time_t = i32;
+                type wchar_t = u16;
             }
             mod c99 {
                 type c_longlong = i64;
@@ -258,7 +307,39 @@ mod types {
                 type mode_t = u16;
                 type ssize_t = i32;
             }
+            mod posix01 { }
+            mod posix08 { }
+            mod bsd44 { }
             mod extra {
+                type BOOL = c_int;
+                type BYTE = u8;
+                type CCHAR = c_char;
+                type CHAR = c_char;
+
+                type DWORD = c_ulong;
+                type DWORDLONG = c_ulonglong;
+
+                type HANDLE = LPVOID;
+                type HMODULE = c_uint;
+
+                type LONG_PTR = c_long;
+
+                type LPCWSTR = *WCHAR;
+                type LPCSTR = *CHAR;
+
+                type LPWSTR = *mutable WCHAR;
+                type LPSTR = *mutable CHAR;
+
+                // Not really, but opaque to us.
+                type LPSECURITY_ATTRIBUTES = LPVOID;
+
+                type LPVOID = *mutable c_void;
+                type LPWORD = *mutable WORD;
+
+                type LRESULT = LONG_PTR;
+                type PBOOL = *mutable BOOL;
+                type WCHAR = wchar_t;
+                type WORD = u16;
             }
         }
     }
@@ -281,6 +362,7 @@ mod types {
                 type ptrdiff_t = i32;
                 type clock_t = u32;
                 type time_t = i32;
+                type wchar_t = i32;
             }
             mod c99 {
                 type c_longlong = i64;
@@ -299,6 +381,9 @@ mod types {
                 type mode_t = u16;
                 type ssize_t = i32;
             }
+            mod posix01 { }
+            mod posix08 { }
+            mod bsd44 { }
             mod extra {
             }
         }
@@ -319,6 +404,7 @@ mod types {
                 type ptrdiff_t = i64;
                 type clock_t = u64;
                 type time_t = i64;
+                type wchar_t = i32;
             }
             mod c99 {
                 type c_longlong = i64;
@@ -337,6 +423,9 @@ mod types {
                 type mode_t = u16;
                 type ssize_t = i64;
             }
+            mod posix01 { }
+            mod posix08 { }
+            mod bsd44 { }
             mod extra {
             }
         }
@@ -367,6 +456,7 @@ mod consts {
             const L_tmpnam : uint = 16_u;
             const TMP_MAX : uint = 32767_u;
         }
+        mod c99 { }
         mod posix88 {
             const O_RDONLY : int = 0;
             const O_WRONLY : int = 1;
@@ -396,9 +486,13 @@ mod consts {
             const STDIN_FILENO : int = 0;
             const STDOUT_FILENO : int = 1;
         }
+        mod posix01 { }
+        mod posix08 { }
+        mod bsd44 { }
         mod extra {
             const O_TEXT : int = 16384;
             const O_BINARY : int = 32768;
+            const O_NOINHERIT: int = 128;
         }
     }
 
@@ -422,6 +516,7 @@ mod consts {
             const L_tmpnam : uint = 20_u;
             const TMP_MAX : uint = 238328_u;
         }
+        mod c99 { }
         mod posix88 {
             const O_RDONLY : int = 0;
             const O_WRONLY : int = 1;
@@ -452,6 +547,9 @@ mod consts {
             const F_TLOCK : int = 2;
             const F_ULOCK : int = 0;
         }
+        mod posix01 { }
+        mod posix08 { }
+        mod bsd44 { }
         mod extra {
             const O_RSYNC : int = 1052672;
             const O_DSYNC : int = 4096;
@@ -478,6 +576,7 @@ mod consts {
             const L_tmpnam : uint = 1024_u;
             const TMP_MAX : uint = 308915776_u;
         }
+        mod c99 { }
         mod posix88 {
             const O_RDONLY : int = 0;
             const O_WRONLY : int = 1;
@@ -511,8 +610,14 @@ mod consts {
             const F_TLOCK : int = 2;
             const F_ULOCK : int = 0;
         }
+        mod posix01 { }
+        mod posix08 { }
+        mod bsd44 { }
         mod extra {
             const O_SYNC : int = 128;
+            const CTL_KERN: int = 1;
+            const KERN_PROC: int = 14;
+            const KERN_PROC_PATHNAME: int = 12;
         }
     }
 
@@ -535,6 +640,7 @@ mod consts {
             const L_tmpnam : uint = 1024_u;
             const TMP_MAX : uint = 308915776_u;
         }
+        mod c99 { }
         mod posix88 {
             const O_RDONLY : int = 0;
             const O_WRONLY : int = 1;
@@ -568,9 +674,13 @@ mod consts {
             const F_TLOCK : int = 2;
             const F_ULOCK : int = 0;
         }
+        mod posix01 { }
+        mod posix08 { }
+        mod bsd44 { }
         mod extra {
             const O_DSYNC : int = 4194304;
             const O_SYNC : int = 128;
+            const F_FULLFSYNC : int = 51;
         }
     }
 }
@@ -725,6 +835,9 @@ mod funcs {
 
             #[link_name = "_pclose"]
             fn pclose(stream: *FILE) -> c_int;
+
+            #[link_name = "_fdopen"]
+            fn fdopen(fd: c_int, mode: *c_char) -> *FILE;
         }
 
         #[nolink]
@@ -765,7 +878,8 @@ mod funcs {
             fn execv(prog: *c_char, argv: **c_char) -> intptr_t;
 
             #[link_name = "_execve"]
-            fn execve(prog: *c_char, argv: **c_char, envp: **c_char) -> c_int;
+            fn execve(prog: *c_char, argv: **c_char,
+                      envp: **c_char) -> c_int;
 
             #[link_name = "_execvp"]
             fn execvp(c: *c_char, argv: **c_char) -> c_int;
@@ -786,7 +900,8 @@ mod funcs {
             fn lseek(fd: c_int, offset: c_long, origin: c_int) -> c_long;
 
             #[link_name = "_pipe"]
-            fn pipe(fds: *c_int, psize: c_uint, textmode: c_int) -> c_int;
+            fn pipe(fds: *mutable c_int, psize: c_uint,
+                    textmode: c_int) -> c_int;
 
             #[link_name = "_read"]
             fn read(fd: c_int, buf: *c_void, count: c_uint) -> c_int;
@@ -823,6 +938,7 @@ mod funcs {
         native mod stdio {
             fn popen(command: *c_char, mode: *c_char) -> *FILE;
             fn pclose(stream: *FILE) -> c_int;
+            fn fdopen(fd: c_int, mode: *c_char) -> *FILE;
         }
 
         #[nolink]
@@ -875,7 +991,7 @@ mod funcs {
             fn lseek(fd: c_int, offset: off_t, whence: c_int) -> off_t;
             fn pathconf(path: *c_char, name: c_int) -> c_long;
             fn pause() -> c_int;
-            fn pipe(fds: *c_int) -> c_int;
+            fn pipe(fds: *mutable c_int) -> c_int;
             fn read(fd: c_int, buf: *c_void, count: size_t) -> ssize_t;
             fn rmdir(path: *c_char) -> c_int;
             fn setgid(gid: gid_t) -> c_int;
@@ -890,7 +1006,101 @@ mod funcs {
             fn write(fd: c_int, buf: *c_void, count: size_t) -> ssize_t;
         }
     }
+
+    #[cfg(target_os = "linux")]
+    #[cfg(target_os = "macos")]
+    #[cfg(target_os = "freebsd")]
+    mod posix01 {
+
+        #[nolink]
+        #[abi = "cdecl"]
+        native mod unistd {
+            fn readlink(path: *c_char, buf: *mutable c_char,
+                        bufsz: size_t) -> ssize_t;
+
+            fn setenv(name: *c_char, val: *c_char,
+                      overwrite: c_int) -> c_int;
+            fn unsetenv(name: *c_char) -> c_int;
+            fn putenv(string: *c_char) -> c_int;
+        }
+    }
+
+    #[cfg(target_os = "win32")]
+    mod posix01 {
+        #[nolink]
+        native mod unistd { }
+    }
+
+
+    #[cfg(target_os = "win32")]
+    #[cfg(target_os = "linux")]
+    #[cfg(target_os = "macos")]
+    #[cfg(target_os = "freebsd")]
+    mod posix08 {
+        #[nolink]
+        native mod unistd { }
+    }
+
+
+    #[cfg(target_os = "macos")]
+    #[cfg(target_os = "freebsd")]
+    #[nolink]
+    #[abi = "cdecl"]
+    native mod bsd44 {
+
+        fn sysctl(name: *c_int, namelen: c_uint,
+                  oldp: *mutable c_void, oldlenp: *mutable size_t,
+                  newp: *c_void, newlen: size_t) -> c_int;
+
+        fn sysctlbyname(name: *c_char,
+                        oldp: *mutable c_void, oldlenp: *mutable size_t,
+                        newp: *c_void, newlen: size_t) -> c_int;
+
+        fn sysctlnametomib(name: *c_char, mibp: *mutable c_int,
+                           sizep: *mutable size_t) -> c_int;
+    }
+
+
+    #[cfg(target_os = "linux")]
+    #[cfg(target_os = "win32")]
+    mod bsd44 {
+    }
+
+
+    #[cfg(target_os = "macos")]
+    #[nolink]
+    #[abi = "cdecl"]
+    native mod extra {
+        fn _NSGetExecutablePath(buf: *mutable c_char,
+                                bufsize: *mutable u32) -> c_int;
+    }
+
+    #[cfg(target_os = "freebsd")]
+    mod extra { }
+
+    #[cfg(target_os = "linux")]
+    mod extra { }
+
+
+    #[cfg(target_os = "win32")]
     mod extra {
+        import types::os::arch::extra::*;
+
+        #[abi = "stdcall"]
+        native mod kernel32 {
+            fn GetEnvironmentVariableA(n: LPCSTR,
+                                       v: LPSTR,
+                                       nsize: DWORD) -> DWORD;
+            fn SetEnvironmentVariableA(n: LPCSTR, v: LPCSTR) -> BOOL;
+            fn GetModuleFileNameA(hModule: HMODULE,
+                                  lpFilename: LPSTR,
+                                  nSize: DWORD) -> DWORD;
+            fn CreateDirectoryA(lpPathName: LPCSTR,
+                                lpSecurityAttributes:
+                                LPSECURITY_ATTRIBUTES) -> BOOL;
+            fn RemoveDirectoryA(lpPathName: LPCSTR) -> BOOL;
+            fn SetCurrentDirectoryA(lpPathName: LPCSTR) -> BOOL;
+        }
     }
 }
 
diff --git a/src/libcore/os.rs b/src/libcore/os.rs
new file mode 100644
index 00000000000..a458fe49a30
--- /dev/null
+++ b/src/libcore/os.rs
@@ -0,0 +1,529 @@
+// Higher-level interfaces to libc::* functions and operating system services.
+//
+// In general these take and return rust types, use rust idioms (enums,
+// closures, vectors) rather than C idioms, and do more extensive safety
+// checks.
+//
+// This module is not meant to only contain 1:1 mappings to libc entries; any
+// os-interface code that is reasonably useful and broadly applicable can go
+// here. Including utility routines that merely build on other os code.
+//
+// We assume the general case is that users do not care, and do not want to
+// be made to care, which operating system they are on. While they may want
+// to special case various special cases -- and so we will not _hide_ the
+// facts of which OS the user is on -- they should be given the opportunity
+// to write OS-ignorant code by default.
+
+import libc::{c_char, c_void, c_int, c_uint, size_t, mode_t, FILE};
+import libc::{close, fclose};
+
+import getcwd = rustrt::rust_getcwd;
+import consts::*;
+
+export close, fclose;
+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;
+
+native mod rustrt {
+    fn rust_env_pairs() -> [str];
+    fn rust_getcwd() -> str;
+    fn rust_path_is_dir(path: str::sbuf) -> c_int;
+    fn rust_path_exists(path: str::sbuf) -> c_int;
+    fn rust_list_files(path: str) -> [str];
+}
+
+
+fn env() -> [(str,str)] {
+    let pairs = [];
+    for p in rustrt::rust_env_pairs() {
+        let vs = str::splitn_char(p, '=', 1u);
+        assert vec::len(vs) == 2u;
+        pairs += [(vs[0], vs[1])];
+    }
+    ret pairs;
+}
+
+fn as_c_charp<T>(s: str, f: fn(*c_char) -> T) -> T {
+    str::as_buf(s) {|b| f(b as *c_char) }
+}
+
+// FIXME: UTF-16-ify this and revive win32 Unicode variant.
+fn getenv(n: str) -> option<str> unsafe {
+    let s = as_c_charp(n, libc::getenv);
+    ret if unsafe::reinterpret_cast(s) == 0 {
+            option::none::<str>
+        } else {
+            let s = unsafe::reinterpret_cast(s);
+            option::some::<str>(str::from_cstr(s))
+        };
+}
+
+#[cfg(target_os = "linux")]
+#[cfg(target_os = "macos")]
+#[cfg(target_os = "freebsd")]
+fn setenv(n: str, v: str) {
+
+    // FIXME: remove this when export globs work properly.
+    import libc::funcs::posix01::unistd::setenv;
+
+    as_c_charp(n) {|nbuf|
+        as_c_charp(v) {|vbuf|
+            setenv(nbuf, vbuf, 1i32);
+        }
+    }
+}
+
+
+#[cfg(target_os = "win32")]
+// FIXME: UTF-16-ify this and switch to the W version.
+fn setenv(n: str, v: str) {
+    // FIXME: remove imports when export globs work properly.
+    import libc::funcs::extra::kernel32;
+    as_c_charp(n) {|nbuf|
+        as_c_charp(v) {|vbuf|
+            kernel32::SetEnvironmentVariableA(nbuf, vbuf);
+        }
+    }
+}
+
+
+fn fdopen(fd: c_int) -> *FILE {
+    ret as_c_charp("r") {|modebuf|
+        libc::fdopen(fd, modebuf)
+    };
+}
+
+
+
+#[cfg(target_os = "linux")]
+#[cfg(target_os = "freebsd")]
+#[cfg(target_os = "macos")]
+fn pipe() -> {in: c_int, out: c_int} {
+    let fds = {mutable in: 0 as c_int,
+               mutable out: 0 as c_int };
+    assert (libc::pipe(ptr::mut_addr_of(fds.in)) == (0 as c_int));
+    ret {in: fds.in, out: fds.out};
+}
+
+
+
+#[cfg(target_os = "win32")]
+fn pipe() -> {in: c_int, out: c_int} {
+    // FIXME: remove this when export globs work properly.
+    import libc::consts::os::extra::*;
+    // Windows pipes work subtly differently than unix pipes, and their
+    // inheritance has to be handled in a different way that I do not fully
+    // understand. Here we explicitly make the pipe non-inheritable, which
+    // means to pass it to a subprocess they need to be duplicated first, as
+    // in rust_run_program.
+    let fds = { mutable in: 0 as c_int,
+               mutable out: 0 as c_int };
+    let res = libc::pipe(ptr::mut_addr_of(fds.in),
+                         1024 as c_uint,
+                         (O_BINARY | O_NOINHERIT) as c_int);
+    assert (res == 0 as c_int);
+    assert (fds.in != -1 as c_int && fds.in != 0 as c_int);
+    assert (fds.out != -1 as c_int && fds.in != 0 as c_int);
+    ret {in: fds.in, out: fds.out};
+}
+
+
+fn dll_filename(base: str) -> str {
+    ret pre() + base + dll_suffix();
+
+    #[cfg(target_os = "macos")]
+    #[cfg(target_os = "linux")]
+    #[cfg(target_os = "freebsd")]
+    fn pre() -> str { "lib" }
+
+    #[cfg(target_os = "win32")]
+    fn pre() -> str { "" }
+}
+
+fn self_exe_path() -> option<path> unsafe {
+    let bufsize = 1023u;
+    let buf = vec::init_elt_mut(bufsize, 0u8 as c_char);
+    // FIXME: This does not handle the case where the buffer is too small
+    ret vec::as_mut_buf(buf) {|pbuf|
+        if load_self(pbuf as *mutable c_char, bufsize as c_uint) {
+            let path = str::from_cstr(pbuf as str::sbuf);
+            option::some(path::dirname(path) + path::path_sep())
+        } else {
+            option::none
+        }
+    };
+
+    #[cfg(target_os = "freebsd")]
+    unsafe fn load_self(pth: *mutable c_char, plen: c_uint) -> bool {
+        // FIXME: remove imports when export globs work properly.
+        import libc::funcs::bsd44::*;
+        import libc::consts::os::extra::*;
+        let mib = [CTL_KERN as c_int,
+                   KERN_PROC as c_int,
+                   KERN_PROC_PATHNAME as c_int, -1 as c_int];
+        ret sysctl(vec::unsafe::to_ptr(mib), vec::len(mib) as c_uint,
+                   pth as *mutable c_void, ptr::mut_addr_of(plen),
+                   ptr::null(), 0u as size_t)
+            == (0 as c_int);
+    }
+
+    #[cfg(target_os = "linux")]
+    unsafe fn load_self(pth: *mutable c_char, plen: c_uint) -> bool {
+        // FIXME: remove imports when export globs work properly.
+        import libc::funcs::posix01::unistd::readlink;
+        as_c_charp("/proc/self/exe") { |proc_self_buf|
+            ret readlink(proc_self_buf, pth, plen as size_t) != -1;
+        }
+    }
+
+    #[cfg(target_os = "win32")]
+    unsafe fn load_self(pth: *mutable c_char, plen: c_uint) -> bool {
+        // FIXME: remove imports when export globs work properly.
+        import libc::types::os::arch::extra::*;
+        import libc::funcs::extra::kernel32;
+        ret kernel32::GetModuleFileNameA(0u, pth, plen) != (0 as DWORD);
+    }
+
+    #[cfg(target_os = "macos")]
+    unsafe fn load_self(pth: *mutable c_char, plen: c_uint) -> bool {
+        // FIXME: remove imports when export globs work properly.
+        import libc::funcs::extra::*;
+        let mplen = plen;
+        ret _NSGetExecutablePath(pth, ptr::mut_addr_of(mplen))
+            == (0 as c_int);
+    }
+
+}
+
+
+/*
+Function: homedir
+
+Returns the path to the user's home directory, if known.
+
+On Unix, returns the value of the "HOME" environment variable if it is set and
+not equal to the empty string.
+
+On Windows, returns the value of the "HOME" environment variable if it is set
+and not equal to the empty string. Otherwise, returns the value of the
+"USERPROFILE" environment variable if it is set and not equal to the empty
+string.
+
+Otherwise, homedir returns option::none.
+*/
+fn homedir() -> option<path> {
+    ret alt getenv("HOME") {
+        some(p) {
+            if !str::is_empty(p) {
+                some(p)
+            } else {
+                secondary()
+            }
+        }
+        none {
+            secondary()
+        }
+    };
+
+    #[cfg(target_os = "linux")]
+    #[cfg(target_os = "macos")]
+    #[cfg(target_os = "freebsd")]
+    fn secondary() -> option<path> {
+        none
+    }
+
+    #[cfg(target_os = "win32")]
+    fn secondary() -> option<path> {
+        option::maybe(none, getenv("USERPROFILE")) {|p|
+            if !str::is_empty(p) {
+                some(p)
+            } else {
+                none
+            }
+        }
+    }
+}
+
+
+
+/*
+Function: path_is_dir
+
+Indicates whether a path represents a directory.
+*/
+fn path_is_dir(p: path) -> bool {
+    ret str::as_buf(p, {|buf|
+        rustrt::rust_path_is_dir(buf) != 0 as c_int
+    });
+}
+
+/*
+Function: path_exists
+
+Indicates whether a path exists.
+*/
+fn path_exists(p: path) -> bool {
+    ret str::as_buf(p, {|buf|
+        rustrt::rust_path_exists(buf) != 0 as c_int
+    });
+}
+
+// FIXME: under Windows, we should prepend the current drive letter to paths
+// that start with a slash.
+/*
+Function: make_absolute
+
+Convert a relative path to an absolute path
+
+If the given path is relative, return it prepended with the current working
+directory. If the given path is already an absolute path, return it
+as is.
+*/
+// NB: this is here rather than in path because it is a form of environment
+// querying; what it does depends on the process working directory, not just
+// the input paths.
+fn make_absolute(p: path) -> path {
+    if path::path_is_absolute(p) {
+        p
+    } else {
+        path::connect(getcwd(), p)
+    }
+}
+
+
+/*
+Function: make_dir
+
+Creates a directory at the specified path.
+*/
+fn make_dir(p: path, mode: c_int) -> bool {
+    ret mkdir(p, mode);
+
+    #[cfg(target_os = "win32")]
+    fn mkdir(_p: path, _mode: c_int) -> bool unsafe {
+        // FIXME: turn mode into something useful?
+        ret as_c_charp(_p, {|buf|
+            // FIXME: remove imports when export globs work properly.
+            import libc::types::os::arch::extra::*;
+            import libc::funcs::extra::kernel32;
+            kernel32::CreateDirectoryA(
+                buf, unsafe::reinterpret_cast(0)) != (0 as BOOL)
+        });
+    }
+
+    #[cfg(target_os = "linux")]
+    #[cfg(target_os = "macos")]
+    #[cfg(target_os = "freebsd")]
+    fn mkdir(p: path, mode: c_int) -> bool {
+        ret as_c_charp(p) {|c|
+            libc::mkdir(c, mode as mode_t) == (0 as c_int)
+        };
+    }
+}
+
+/*
+Function: list_dir
+
+Lists the contents of a directory.
+*/
+fn list_dir(p: path) -> [str] {
+
+    #[cfg(target_os = "linux")]
+    #[cfg(target_os = "macos")]
+    #[cfg(target_os = "freebsd")]
+    fn star() -> str { "" }
+
+    #[cfg(target_os = "win32")]
+    fn star() -> str { "*" }
+
+    let p = p;
+    let pl = str::len(p);
+    if pl == 0u || (p[pl - 1u] as char != path::consts::path_sep
+                    && p[pl - 1u] as char != path::consts::alt_path_sep) {
+        p += path::path_sep();
+    }
+    let full_paths: [str] = [];
+    for filename: str in rustrt::rust_list_files(p + star()) {
+        if !str::eq(filename, ".") {
+            if !str::eq(filename, "..") {
+                full_paths += [p + filename];
+            }
+        }
+    }
+    ret full_paths;
+}
+
+/*
+Function: remove_dir
+
+Removes a directory at the specified path.
+*/
+fn remove_dir(p: path) -> bool {
+   ret rmdir(p);
+
+    #[cfg(target_os = "win32")]
+    fn rmdir(p: path) -> bool {
+        // FIXME: remove imports when export globs work properly.
+        import libc::funcs::extra::kernel32;
+        import libc::types::os::arch::extra::*;
+        ret as_c_charp(p) {|buf|
+            kernel32::RemoveDirectoryA(buf) != (0 as BOOL)
+        };
+    }
+
+    #[cfg(target_os = "linux")]
+    #[cfg(target_os = "macos")]
+    #[cfg(target_os = "freebsd")]
+    fn rmdir(p: path) -> bool {
+        ret as_c_charp(p) {|buf|
+            libc::rmdir(buf) == (0 as c_int)
+        };
+    }
+}
+
+fn change_dir(p: path) -> bool {
+    ret chdir(p);
+
+    #[cfg(target_os = "win32")]
+    fn chdir(p: path) -> bool {
+        // FIXME: remove imports when export globs work properly.
+        import libc::funcs::extra::kernel32;
+        import libc::types::os::arch::extra::*;
+        ret as_c_charp(p) {|buf|
+            kernel32::SetCurrentDirectoryA(buf) != (0 as BOOL)
+        };
+    }
+
+    #[cfg(target_os = "linux")]
+    #[cfg(target_os = "macos")]
+    #[cfg(target_os = "freebsd")]
+    fn chdir(p: path) -> bool {
+        ret as_c_charp(p) {|buf|
+            libc::chdir(buf) == (0 as c_int)
+        };
+    }
+}
+
+
+
+#[cfg(target_os = "macos")]
+mod consts {
+    fn sysname() -> str { "macos" }
+    fn exe_suffix() -> str { "" }
+    fn dll_suffix() -> str { ".dylib" }
+}
+
+#[cfg(target_os = "freebsd")]
+mod consts {
+    fn sysname() -> str { "freebsd" }
+    fn exe_suffix() -> str { "" }
+    fn dll_suffix() -> str { ".so" }
+}
+
+#[cfg(target_os = "linux")]
+mod consts {
+    fn sysname() -> str { "linux" }
+    fn exe_suffix() -> str { "" }
+    fn dll_suffix() -> str { ".so" }
+}
+
+#[cfg(target_os = "win32")]
+mod consts {
+    fn sysname() -> str { "win32" }
+    fn exe_suffix() -> str { ".exe" }
+    fn dll_suffix() -> str { ".dll" }
+}
+
+
+
+
+#[cfg(test)]
+mod tests {
+
+    import os = new_os;
+
+    #[test]
+    fn test() {
+        assert (!path::path_is_absolute("test-path"));
+
+        log(debug, "Current working directory: " + getcwd());
+
+        log(debug, make_absolute("test-path"));
+        log(debug, make_absolute("/usr/bin"));
+    }
+
+    #[test]
+    #[cfg(target_os = "linux")]
+    #[cfg(target_os = "macos")]
+    #[cfg(target_os = "freebsd")]
+    fn homedir() {
+        let oldhome = getenv("HOME");
+
+        setenv("HOME", "/home/MountainView");
+        assert os::homedir() == some("/home/MountainView");
+
+        setenv("HOME", "");
+        assert os::homedir() == none;
+
+        option::may(oldhome, {|s| setenv("HOME", s)});
+    }
+
+    #[test]
+    #[cfg(target_os = "win32")]
+    fn homedir() {
+
+        let oldhome = getenv("HOME");
+        let olduserprofile = getenv("USERPROFILE");
+
+        setenv("HOME", "");
+        setenv("USERPROFILE", "");
+
+        assert os::homedir() == none;
+
+        setenv("HOME", "/home/MountainView");
+        assert os::homedir() == some("/home/MountainView");
+
+        setenv("HOME", "");
+
+        setenv("USERPROFILE", "/home/MountainView");
+        assert os::homedir() == some("/home/MountainView");
+
+        setenv("USERPROFILE", "/home/MountainView");
+        assert os::homedir() == some("/home/MountainView");
+
+        setenv("HOME", "/home/MountainView");
+        setenv("USERPROFILE", "/home/PaloAlto");
+        assert os::homedir() == some("/home/MountainView");
+
+        option::may(oldhome, {|s| setenv("HOME", s)});
+        option::may(olduserprofile, {|s| setenv("USERPROFILE", s)});
+    }
+
+    // Issue #712
+    #[test]
+    fn test_list_dir_no_invalid_memory_access() { os::list_dir("."); }
+
+    #[test]
+    fn list_dir() {
+        let dirs = os::list_dir(".");
+        // Just assuming that we've got some contents in the current directory
+        assert (vec::len(dirs) > 0u);
+
+        for dir in dirs { log(debug, dir); }
+    }
+
+    #[test]
+    fn path_is_dir() {
+        assert (os::path_is_dir("."));
+        assert (!os::path_is_dir("test/stdtest/fs.rs"));
+    }
+
+    #[test]
+    fn path_exists() {
+        assert (os::path_exists("."));
+        assert (!os::path_exists("test/nonexistent-bogus-path"));
+    }
+
+}
\ No newline at end of file
diff --git a/src/libcore/path.rs b/src/libcore/path.rs
new file mode 100644
index 00000000000..d5a80c39c7f
--- /dev/null
+++ b/src/libcore/path.rs
@@ -0,0 +1,505 @@
+/*
+Module: path
+
+Path data type and helper functions.
+*/
+
+#[cfg(target_os = "macos")]
+#[cfg(target_os = "freebsd")]
+#[cfg(target_os = "linux")]
+mod consts {
+    const path_sep: char = '/';
+    const alt_path_sep: char = '/';
+}
+
+#[cfg(target_os = "win32")]
+mod consts {
+    const path_sep: char = '/';
+    const alt_path_sep: char = '\\';
+}
+
+/*
+Function: path_is_absolute
+
+Indicates whether a path is absolute.
+
+A path is considered absolute if it begins at the filesystem root ("/") or,
+on Windows, begins with a drive letter.
+*/
+#[cfg(target_os = "macos")]
+#[cfg(target_os = "freebsd")]
+#[cfg(target_os = "linux")]
+fn path_is_absolute(p: path) -> bool {
+    str::char_at(p, 0u) == '/'
+}
+
+#[cfg(target_os = "win32")]
+fn path_is_absolute(p: str) -> bool {
+    ret str::char_at(p, 0u) == '/' ||
+        str::char_at(p, 1u) == ':'
+        && (str::char_at(p, 2u) == consts::path_sep
+            || str::char_at(p, 2u) == consts::alt_path_sep);
+}
+
+
+
+/*
+Function: path_sep
+
+Get the default path separator for the host platform
+*/
+fn path_sep() -> str { ret str::from_char(consts::path_sep); }
+
+// FIXME: This type should probably be constrained
+/*
+Type: path
+
+A path or fragment of a filesystem path
+*/
+type path = str;
+
+fn split_dirname_basename (pp: path) -> {dirname: str, basename: str} {
+    alt str::rfind(pp, {|ch|
+        ch == consts::path_sep || ch == consts::alt_path_sep
+    }) {
+      some(i) {
+        {dirname: str::slice(pp, 0u, i),
+         basename: str::slice(pp, i + 1u, str::len(pp))}
+      }
+      none { {dirname: ".", basename: pp} }
+    }
+}
+
+/*
+Function: dirname
+
+Get the directory portion of a path
+
+Returns all of the path up to, but excluding, the final path separator.
+The dirname of "/usr/share" will be "/usr", but the dirname of
+"/usr/share/" is "/usr/share".
+
+If the path is not prefixed with a directory, then "." is returned.
+*/
+fn dirname(pp: path) -> path {
+    ret split_dirname_basename(pp).dirname;
+}
+
+/*
+Function: basename
+
+Get the file name portion of a path
+
+Returns the portion of the path after the final path separator.
+The basename of "/usr/share" will be "share". If there are no
+path separators in the path then the returned path is identical to
+the provided path. If an empty path is provided or the path ends
+with a path separator then an empty path is returned.
+*/
+fn basename(pp: path) -> path {
+    ret split_dirname_basename(pp).basename;
+}
+
+// FIXME: Need some typestate to avoid bounds check when len(pre) == 0
+/*
+Function: connect
+
+Connects to path segments
+
+Given paths `pre` and `post, removes any trailing path separator on `pre` and
+any leading path separator on `post`, and returns the concatenation of the two
+with a single path separator between them.
+*/
+
+fn connect(pre: path, post: path) -> path unsafe {
+    let pre_ = pre;
+    let post_ = post;
+    let sep = consts::path_sep as u8;
+    let pre_len  = str::len(pre);
+    let post_len = str::len(post);
+    if pre_len > 1u && pre[pre_len-1u] == sep { str::unsafe::pop_byte(pre_); }
+    if post_len > 1u && post[0] == sep { str::unsafe::shift_byte(post_); }
+    ret pre_ + path_sep() + post_;
+}
+
+/*
+Function: connect_many
+
+Connects a vector of path segments into a single path.
+
+Inserts path separators as needed.
+*/
+fn connect_many(paths: [path]) -> path {
+    ret if vec::len(paths) == 1u {
+        paths[0]
+    } else {
+        let rest = vec::slice(paths, 1u, vec::len(paths));
+        connect(paths[0], connect_many(rest))
+    }
+}
+
+/*
+Function: split
+
+Split a path into it's individual components
+
+Splits a given path by path separators and returns a vector containing
+each piece of the path. On Windows, if the path is absolute then
+the first element of the returned vector will be the drive letter
+followed by a colon.
+*/
+fn split(p: path) -> [path] {
+    str::split_nonempty(p, {|c|
+        c == consts::path_sep || c == consts::alt_path_sep
+    })
+}
+
+/*
+Function: splitext
+
+Split a path into a pair of strings with the first element being the filename
+without the extension and the second being either empty or the file extension
+including the period. Leading periods in the basename are ignored.  If the
+path includes directory components then they are included in the filename part
+of the result pair.
+*/
+fn splitext(p: path) -> (str, str) {
+    if str::is_empty(p) { ("", "") }
+    else {
+        let parts = str::split_char(p, '.');
+        if vec::len(parts) > 1u {
+            let base = str::connect(vec::init(parts), ".");
+            let ext = "." + vec::last_total(parts);
+
+            fn is_dotfile(base: str) -> bool {
+                str::is_empty(base)
+                    || str::ends_with(
+                        base, str::from_char(consts::path_sep))
+                    || str::ends_with(
+                        base, str::from_char(consts::alt_path_sep))
+            }
+
+            fn ext_contains_sep(ext: str) -> bool {
+                vec::len(split(ext)) > 1u
+            }
+
+            fn no_basename(ext: str) -> bool {
+                str::ends_with(
+                    ext, str::from_char(consts::path_sep))
+                    || str::ends_with(
+                        ext, str::from_char(consts::alt_path_sep))
+            }
+
+            if is_dotfile(base)
+                || ext_contains_sep(ext)
+                || no_basename(ext) {
+                (p, "")
+            } else {
+                (base, ext)
+            }
+        } else {
+            (p, "")
+        }
+    }
+}
+
+/*
+Function: normalize
+
+Removes extra "." and ".." entries from paths.
+
+Does not follow symbolic links.
+*/
+fn normalize(p: path) -> path {
+    let s = split(p);
+    let s = strip_dots(s);
+    let s = rollup_doubledots(s);
+
+    let s = if check vec::is_not_empty(s) {
+        connect_many(s)
+    } else {
+        ""
+    };
+    let s = reabsolute(p, s);
+    let s = reterminate(p, s);
+
+    let s = if str::len(s) == 0u {
+        "."
+    } else {
+        s
+    };
+
+    ret s;
+
+    fn strip_dots(s: [path]) -> [path] {
+        vec::filter_map(s, { |elem|
+            if elem == "." {
+                option::none
+            } else {
+                option::some(elem)
+            }
+        })
+    }
+
+    fn rollup_doubledots(s: [path]) -> [path] {
+        if vec::is_empty(s) {
+            ret [];
+        }
+
+        let t = [];
+        let i = vec::len(s);
+        let skip = 0;
+        do {
+            i -= 1u;
+            if s[i] == ".." {
+                skip += 1;
+            } else {
+                if skip == 0 {
+                    t += [s[i]];
+                } else {
+                    skip -= 1;
+                }
+            }
+        } while i != 0u;
+        let t = vec::reversed(t);
+        while skip > 0 {
+            t += [".."];
+            skip -= 1;
+        }
+        ret t;
+    }
+
+    #[cfg(target_os = "linux")]
+    #[cfg(target_os = "macos")]
+    #[cfg(target_os = "freebsd")]
+    fn reabsolute(orig: path, new: path) -> path {
+        if path_is_absolute(orig) {
+            path_sep() + new
+        } else {
+            new
+        }
+    }
+
+    #[cfg(target_os = "win32")]
+    fn reabsolute(orig: path, new: path) -> path {
+       if path_is_absolute(orig) && orig[0] == consts::path_sep as u8 {
+           str::from_char(consts::path_sep) + new
+       } else {
+           new
+       }
+    }
+
+    fn reterminate(orig: path, new: path) -> path {
+        let last = orig[str::len(orig) - 1u];
+        if last == consts::path_sep as u8
+            || last == consts::path_sep as u8 {
+            ret new + path_sep();
+        } else {
+            ret new;
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    #[test]
+    fn test_connect() {
+        let slash = path_sep();
+        log(error, connect("a", "b"));
+        assert (connect("a", "b") == "a" + slash + "b");
+        assert (connect("a" + slash, "b") == "a" + slash + "b");
+    }
+
+    fn ps() -> str {
+        path_sep()
+    }
+
+    fn aps() -> str {
+        "/"
+    }
+
+    #[test]
+    fn split1() {
+        let actual = split("a" + ps() + "b");
+        let expected = ["a", "b"];
+        assert actual == expected;
+    }
+
+    #[test]
+    fn split2() {
+        let actual = split("a" + aps() + "b");
+        let expected = ["a", "b"];
+        assert actual == expected;
+    }
+
+    #[test]
+    fn split3() {
+        let actual = split(ps() + "a" + ps() + "b");
+        let expected = ["a", "b"];
+        assert actual == expected;
+    }
+
+    #[test]
+    fn split4() {
+        let actual = split("a" + ps() + "b" + aps() + "c");
+        let expected = ["a", "b", "c"];
+        assert actual == expected;
+    }
+
+    #[test]
+    fn normalize1() {
+        let actual = normalize("a/b/..");
+        let expected = "a";
+        assert actual == expected;
+    }
+
+    #[test]
+    fn normalize2() {
+        let actual = normalize("/a/b/..");
+        let expected = "/a";
+        assert actual == expected;
+    }
+
+    #[test]
+    fn normalize3() {
+        let actual = normalize("a/../b");
+        let expected = "b";
+        assert actual == expected;
+    }
+
+    #[test]
+    fn normalize4() {
+        let actual = normalize("/a/../b");
+        let expected = "/b";
+        assert actual == expected;
+    }
+
+    #[test]
+    fn normalize5() {
+        let actual = normalize("a/.");
+        let expected = "a";
+        assert actual == expected;
+    }
+
+    #[test]
+    fn normalize6() {
+        let actual = normalize("a/./b/");
+        let expected = "a/b/";
+        assert actual == expected;
+    }
+
+    #[test]
+    fn normalize7() {
+        let actual = normalize("a/..");
+        let expected = ".";
+        assert actual == expected;
+    }
+
+    #[test]
+    fn normalize8() {
+        let actual = normalize("../../..");
+        let expected = "../../..";
+        assert actual == expected;
+    }
+
+    #[test]
+    fn normalize9() {
+        let actual = normalize("a/b/../../..");
+        let expected = "..";
+        assert actual == expected;
+    }
+
+    #[test]
+    fn normalize10() {
+        let actual = normalize("/a/b/c/../d/./../../e/");
+        let expected = "/a/e/";
+        log(error, actual);
+        assert actual == expected;
+    }
+
+    #[test]
+    fn normalize11() {
+        let actual = normalize("/a/..");
+        let expected = "/";
+        assert actual == expected;
+    }
+
+    #[test]
+    #[cfg(target_os = "win32")]
+    fn normalize12() {
+        let actual = normalize("C:/whatever");
+        let expected = "C:/whatever";
+        log(error, actual);
+        assert actual == expected;
+    }
+
+    #[test]
+    #[cfg(target_os = "win32")]
+    fn path_is_absolute_win32() {
+        assert path_is_absolute("C:/whatever");
+    }
+
+    #[test]
+    fn splitext_empty() {
+        let (base, ext) = splitext("");
+        assert base == "";
+        assert ext == "";
+    }
+
+    #[test]
+    fn splitext_ext() {
+        let (base, ext) = splitext("grum.exe");
+        assert base == "grum";
+        assert ext == ".exe";
+    }
+
+    #[test]
+    fn splitext_noext() {
+        let (base, ext) = splitext("grum");
+        assert base == "grum";
+        assert ext == "";
+    }
+
+    #[test]
+    fn splitext_dotfile() {
+        let (base, ext) = splitext(".grum");
+        assert base == ".grum";
+        assert ext == "";
+    }
+
+    #[test]
+    fn splitext_path_ext() {
+        let (base, ext) = splitext("oh/grum.exe");
+        assert base == "oh/grum";
+        assert ext == ".exe";
+    }
+
+    #[test]
+    fn splitext_path_noext() {
+        let (base, ext) = splitext("oh/grum");
+        assert base == "oh/grum";
+        assert ext == "";
+    }
+
+    #[test]
+    fn splitext_dot_in_path() {
+        let (base, ext) = splitext("oh.my/grum");
+        assert base == "oh.my/grum";
+        assert ext == "";
+    }
+
+    #[test]
+    fn splitext_nobasename() {
+        let (base, ext) = splitext("oh.my/");
+        assert base == "oh.my/";
+        assert ext == "";
+    }
+}
+
+// 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 16f5e2370be..5be3cb64037 100644
--- a/src/libcore/str.rs
+++ b/src/libcore/str.rs
@@ -1390,7 +1390,7 @@ fn as_buf<T>(s: str, f: fn(sbuf) -> T) -> T unsafe {
 /*
 Type: sbuf
 
-An unsafe buffer of bytes. Corresponds to a C char pointer.
+An unsafe buffer of bytes.
 */
 type sbuf = *u8;
 
diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs
index a101f5f3ba2..27163e9f190 100644
--- a/src/libcore/vec.rs
+++ b/src/libcore/vec.rs
@@ -996,6 +996,10 @@ fn as_buf<E,T>(v: [const E], f: fn(*E) -> T) -> T unsafe {
     let buf = unsafe::to_ptr(v); f(buf)
 }
 
+fn as_mut_buf<E,T>(v: [mutable E], f: fn(*mutable E) -> T) -> T unsafe {
+    let buf = unsafe::to_ptr(v) as *mutable E; f(buf)
+}
+
 impl vec_len<T> for [T] {
     fn len() -> uint { len(self) }
 }