about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorKevin Ballard <kevin@sb.org>2013-09-26 17:21:59 -0700
committerKevin Ballard <kevin@sb.org>2013-10-15 21:56:54 -0700
commit73d3d00ec437f87ac665b4e4da3bedec8ce4f9ef (patch)
tree7050b2b93e3c58d7766e9aecd7e973ea88d9210e /src/libstd
parent6741241f4046aea4014b1a23618593fb481c8606 (diff)
downloadrust-73d3d00ec437f87ac665b4e4da3bedec8ce4f9ef.tar.gz
rust-73d3d00ec437f87ac665b4e4da3bedec8ce4f9ef.zip
path2: Replace the path module outright
Remove the old path.
Rename path2 to path.
Update all clients for the new path.

Also make some miscellaneous changes to the Path APIs to help the
adoption process.
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/io.rs35
-rw-r--r--src/libstd/os.rs274
-rw-r--r--src/libstd/path.rs1507
-rw-r--r--src/libstd/path/mod.rs (renamed from src/libstd/path2/mod.rs)385
-rw-r--r--src/libstd/path/posix.rs (renamed from src/libstd/path2/posix.rs)412
-rw-r--r--src/libstd/path/windows.rs (renamed from src/libstd/path2/windows.rs)451
-rw-r--r--src/libstd/prelude.rs5
-rw-r--r--src/libstd/rt/io/file.rs46
-rw-r--r--src/libstd/rt/io/support.rs4
-rw-r--r--src/libstd/rt/test.rs8
-rw-r--r--src/libstd/rt/uv/file.rs12
-rw-r--r--src/libstd/rt/uv/uvio.rs14
-rw-r--r--src/libstd/run.rs8
-rw-r--r--src/libstd/std.rs1
-rw-r--r--src/libstd/unstable/dynamic_lib.rs2
15 files changed, 1189 insertions, 1975 deletions
diff --git a/src/libstd/io.rs b/src/libstd/io.rs
index 7160496f4ab..434d781805b 100644
--- a/src/libstd/io.rs
+++ b/src/libstd/io.rs
@@ -60,12 +60,11 @@ use num;
 use ops::Drop;
 use option::{Some, None};
 use os;
-use path::Path;
+use path::{Path,GenericPath};
 use ptr;
 use result::{Result, Ok, Err};
 use str::{StrSlice, OwnedStr};
 use str;
-use to_str::ToStr;
 use uint;
 use vec::{MutableVector, ImmutableVector, OwnedVector, OwnedCopyableVector, CopyableVector};
 use vec;
@@ -1069,7 +1068,9 @@ pub fn file_reader(path: &Path) -> Result<@Reader, ~str> {
     };
 
     if f as uint == 0u {
-        Err(~"error opening " + path.to_str())
+        do path.with_display_str |p| {
+            Err(~"error opening " + p)
+        }
     } else {
         Ok(FILE_reader(f, true))
     }
@@ -1335,7 +1336,7 @@ pub fn mk_file_writer(path: &Path, flags: &[FileFlag])
         }
     };
     if fd < (0 as c_int) {
-        Err(format!("error opening {}: {}", path.to_str(), os::last_os_error()))
+        Err(format!("error opening {}: {}", path.display(), os::last_os_error()))
     } else {
         Ok(fd_writer(fd, true))
     }
@@ -1752,7 +1753,7 @@ pub fn read_whole_file_str(file: &Path) -> Result<~str, ~str> {
         if str::is_utf8(bytes) {
             Ok(str::from_utf8(bytes))
         } else {
-            Err(file.to_str() + " is not UTF-8")
+            Err(file.to_display_str() + " is not UTF-8")
         }
     }
 }
@@ -1892,8 +1893,8 @@ mod tests {
 
     #[test]
     fn test_simple() {
-        let tmpfile = &Path("tmp/lib-io-test-simple.tmp");
-        debug2!("{:?}", tmpfile);
+        let tmpfile = &Path::from_str("tmp/lib-io-test-simple.tmp");
+        debug2!("{}", tmpfile.display());
         let frood: ~str =
             ~"A hoopy frood who really knows where his towel is.";
         debug2!("{}", frood.clone());
@@ -1910,7 +1911,7 @@ mod tests {
     #[test]
     fn test_each_byte_each_char_file() {
         // Issue #5056 -- shouldn't include trailing EOF.
-        let path = Path("tmp/lib-io-test-each-byte-each-char-file.tmp");
+        let path = Path::from_str("tmp/lib-io-test-each-byte-each-char-file.tmp");
 
         {
             // create empty, enough to reproduce a problem
@@ -2010,7 +2011,7 @@ mod tests {
 
     #[test]
     fn file_reader_not_exist() {
-        match io::file_reader(&Path("not a file")) {
+        match io::file_reader(&Path::from_str("not a file")) {
           Err(e) => {
             assert_eq!(e, ~"error opening not a file");
           }
@@ -2021,7 +2022,7 @@ mod tests {
     #[test]
     #[should_fail]
     fn test_read_buffer_too_small() {
-        let path = &Path("tmp/lib-io-test-read-buffer-too-small.tmp");
+        let path = &Path::from_str("tmp/lib-io-test-read-buffer-too-small.tmp");
         // ensure the file exists
         io::file_writer(path, [io::Create]).unwrap();
 
@@ -2032,7 +2033,7 @@ mod tests {
 
     #[test]
     fn test_read_buffer_big_enough() {
-        let path = &Path("tmp/lib-io-test-read-buffer-big-enough.tmp");
+        let path = &Path::from_str("tmp/lib-io-test-read-buffer-big-enough.tmp");
         // ensure the file exists
         io::file_writer(path, [io::Create]).unwrap();
 
@@ -2043,7 +2044,7 @@ mod tests {
 
     #[test]
     fn test_write_empty() {
-        let file = io::file_writer(&Path("tmp/lib-io-test-write-empty.tmp"),
+        let file = io::file_writer(&Path::from_str("tmp/lib-io-test-write-empty.tmp"),
                                    [io::Create]).unwrap();
         file.write([]);
     }
@@ -2075,7 +2076,7 @@ mod tests {
 
     #[test]
     fn test_read_write_le() {
-        let path = Path("tmp/lib-io-test-read-write-le.tmp");
+        let path = Path::from_str("tmp/lib-io-test-read-write-le.tmp");
         let uints = [0, 1, 2, 42, 10_123, 100_123_456, u64::max_value];
 
         // write the ints to the file
@@ -2097,7 +2098,7 @@ mod tests {
 
     #[test]
     fn test_read_write_be() {
-        let path = Path("tmp/lib-io-test-read-write-be.tmp");
+        let path = Path::from_str("tmp/lib-io-test-read-write-be.tmp");
         let uints = [0, 1, 2, 42, 10_123, 100_123_456, u64::max_value];
 
         // write the ints to the file
@@ -2119,7 +2120,7 @@ mod tests {
 
     #[test]
     fn test_read_be_int_n() {
-        let path = Path("tmp/lib-io-test-read-be-int-n.tmp");
+        let path = Path::from_str("tmp/lib-io-test-read-be-int-n.tmp");
         let ints = [i32::min_value, -123456, -42, -5, 0, 1, i32::max_value];
 
         // write the ints to the file
@@ -2143,7 +2144,7 @@ mod tests {
 
     #[test]
     fn test_read_f32() {
-        let path = Path("tmp/lib-io-test-read-f32.tmp");
+        let path = Path::from_str("tmp/lib-io-test-read-f32.tmp");
         //big-endian floating-point 8.1250
         let buf = ~[0x41, 0x02, 0x00, 0x00];
 
@@ -2161,7 +2162,7 @@ mod tests {
 
     #[test]
     fn test_read_write_f32() {
-        let path = Path("tmp/lib-io-test-read-write-f32.tmp");
+        let path = Path::from_str("tmp/lib-io-test-read-write-f32.tmp");
         let f:f32 = 8.1250;
 
         {
diff --git a/src/libstd/os.rs b/src/libstd/os.rs
index b7921d7527b..bfde1a86771 100644
--- a/src/libstd/os.rs
+++ b/src/libstd/os.rs
@@ -28,7 +28,7 @@
 
 #[allow(missing_doc)];
 
-use c_str::ToCStr;
+use c_str::{CString, ToCStr};
 use clone::Clone;
 use container::Container;
 use io;
@@ -78,22 +78,7 @@ pub fn getcwd() -> Path {
                 fail2!()
             }
 
-            Path(str::raw::from_c_str(buf as *c_char))
-        }
-    }
-}
-
-// FIXME: move these to str perhaps? #2620
-
-pub fn fill_charp_buf(f: &fn(*mut c_char, size_t) -> bool) -> Option<~str> {
-    let mut buf = [0 as c_char, .. TMPBUF_SZ];
-    do buf.as_mut_buf |b, sz| {
-        if f(b, sz as size_t) {
-            unsafe {
-                Some(str::raw::from_c_str(b as *c_char))
-            }
-        } else {
-            None
+            GenericPath::from_c_str(CString::new(buf as *c_char, false))
         }
     }
 }
@@ -451,70 +436,89 @@ pub fn dll_filename(base: &str) -> ~str {
 pub fn self_exe_path() -> Option<Path> {
 
     #[cfg(target_os = "freebsd")]
-    fn load_self() -> Option<~str> {
+    fn load_self() -> Option<~[u8]> {
         #[fixed_stack_segment]; #[inline(never)];
         unsafe {
             use libc::funcs::bsd44::*;
             use libc::consts::os::extra::*;
-            do fill_charp_buf() |buf, sz| {
-                let mib = ~[CTL_KERN as c_int,
-                           KERN_PROC as c_int,
-                           KERN_PROC_PATHNAME as c_int, -1 as c_int];
-                let mut sz = sz;
+            let mib = ~[CTL_KERN as c_int,
+                        KERN_PROC as c_int,
+                        KERN_PROC_PATHNAME as c_int, -1 as c_int];
+            let mut sz: size_t = 0;
+            let err = sysctl(vec::raw::to_ptr(mib), mib.len() as ::libc::c_uint,
+                             ptr::mut_null(), &mut sz, ptr::null(), 0u as size_t);
+            if err != 0 { return None; }
+            if sz == 0 { return None; }
+            let mut v: ~[u8] = vec::with_capacity(sz as uint);
+            let err = do v.as_mut_buf |buf,_| {
                 sysctl(vec::raw::to_ptr(mib), mib.len() as ::libc::c_uint,
-                       buf as *mut c_void, &mut sz, ptr::null(),
-                       0u as size_t) == (0 as c_int)
-            }
+                       buf as *mut c_void, &mut sz, ptr::null(), 0u as size_t)
+            };
+            if err != 0 { return None; }
+            if sz == 0 { return None; }
+            vec::raw::set_len(&mut v, sz as uint - 1); // chop off trailing NUL
+            Some(v)
         }
     }
 
     #[cfg(target_os = "linux")]
     #[cfg(target_os = "android")]
-    fn load_self() -> Option<~str> {
+    fn load_self() -> Option<~[u8]> {
         #[fixed_stack_segment]; #[inline(never)];
         unsafe {
             use libc::funcs::posix01::unistd::readlink;
 
-            let mut path = [0 as c_char, .. TMPBUF_SZ];
-
-            do path.as_mut_buf |buf, len| {
-                let len = do "/proc/self/exe".with_c_str |proc_self_buf| {
-                    readlink(proc_self_buf, buf, len as size_t) as uint
-                };
+            let mut path: ~[u8] = vec::with_capacity(TMPBUF_SZ);
 
-                if len == -1 {
-                    None
-                } else {
-                    Some(str::raw::from_buf_len(buf as *u8, len))
+            let len = do path.as_mut_buf |buf, _| {
+                do "/proc/self/exe".with_c_str |proc_self_buf| {
+                    readlink(proc_self_buf, buf as *mut c_char, TMPBUF_SZ as size_t) as uint
                 }
+            };
+            if len == -1 {
+                None
+            } else {
+                vec::raw::set_len(&mut path, len as uint);
+                Some(path)
             }
         }
     }
 
     #[cfg(target_os = "macos")]
-    fn load_self() -> Option<~str> {
+    fn load_self() -> Option<~[u8]> {
         #[fixed_stack_segment]; #[inline(never)];
         unsafe {
-            do fill_charp_buf() |buf, sz| {
-                let mut sz = sz as u32;
-                libc::funcs::extra::_NSGetExecutablePath(
-                    buf, &mut sz) == (0 as c_int)
-            }
+            use libc::funcs::extra::_NSGetExecutablePath;
+            let mut sz: u32 = 0;
+            _NSGetExecutablePath(ptr::mut_null(), &mut sz);
+            if sz == 0 { return None; }
+            let mut v: ~[u8] = vec::with_capacity(sz as uint);
+            let err = do v.as_mut_buf |buf,_| {
+                _NSGetExecutablePath(buf as *mut i8, &mut sz)
+            };
+            if err != 0 { return None; }
+            vec::raw::set_len(&mut v, sz as uint - 1); // chop off trailing NUL
+            Some(v)
         }
     }
 
     #[cfg(windows)]
-    fn load_self() -> Option<~str> {
+    fn load_self() -> Option<~[u8]> {
         #[fixed_stack_segment]; #[inline(never)];
         unsafe {
             use os::win32::fill_utf16_buf_and_decode;
             do fill_utf16_buf_and_decode() |buf, sz| {
                 libc::GetModuleFileNameW(0u as libc::DWORD, buf, sz)
-            }
+            }.map_move(|s| s.into_bytes())
         }
     }
 
-    load_self().map(|path| Path(path).dir_path())
+    load_self().and_then(|path| Path::from_vec_opt(path).map(|p| p.dir_path()))
+}
+
+
+/**
+ * Returns the path to the user's home directory, if known.
 }
 
 
@@ -532,13 +536,10 @@ pub fn self_exe_path() -> Option<Path> {
  * Otherwise, homedir returns option::none.
  */
 pub fn homedir() -> Option<Path> {
+    // FIXME (#7188): getenv needs a ~[u8] variant
     return match getenv("HOME") {
-        Some(ref p) => if !p.is_empty() {
-          Some(Path(*p))
-        } else {
-          secondary()
-        },
-        None => secondary()
+        Some(ref p) if !p.is_empty() => Path::from_str_opt(*p),
+        _ => secondary()
     };
 
     #[cfg(unix)]
@@ -550,7 +551,7 @@ pub fn homedir() -> Option<Path> {
     fn secondary() -> Option<Path> {
         do getenv("USERPROFILE").and_then |p| {
             if !p.is_empty() {
-                Some(Path(p))
+                Path::from_str_opt(p)
             } else {
                 None
             }
@@ -579,7 +580,7 @@ pub fn tmpdir() -> Path {
                 if x.is_empty() {
                     None
                 } else {
-                    Some(Path(x))
+                    Path::from_str_opt(x)
                 },
             _ => None
         }
@@ -588,9 +589,9 @@ pub fn tmpdir() -> Path {
     #[cfg(unix)]
     fn lookup() -> Path {
         if cfg!(target_os = "android") {
-            Path("/data/tmp")
+            Path::from_str("/data/tmp")
         } else {
-            getenv_nonempty("TMPDIR").unwrap_or(Path("/tmp"))
+            getenv_nonempty("TMPDIR").unwrap_or(Path::from_str("/tmp"))
         }
     }
 
@@ -599,7 +600,7 @@ pub fn tmpdir() -> Path {
         getenv_nonempty("TMP").or(
             getenv_nonempty("TEMP").or(
                 getenv_nonempty("USERPROFILE").or(
-                   getenv_nonempty("WINDIR")))).unwrap_or(Path("C:\\Windows"))
+                   getenv_nonempty("WINDIR")))).unwrap_or(Path::from_str("C:\\Windows"))
     }
 }
 
@@ -607,7 +608,7 @@ pub fn tmpdir() -> Path {
 pub fn walk_dir(p: &Path, f: &fn(&Path) -> bool) -> bool {
     let r = list_dir(p);
     r.iter().advance(|q| {
-        let path = &p.push(*q);
+        let path = &p.join_path(q);
         f(path) && (!path_is_dir(path) || walk_dir(path, |p| f(p)))
     })
 }
@@ -643,10 +644,12 @@ pub fn path_exists(p: &Path) -> bool {
 // querying; what it does depends on the process working directory, not just
 // the input paths.
 pub fn make_absolute(p: &Path) -> Path {
-    if p.is_absolute {
-        (*p).clone()
+    if p.is_absolute() {
+        p.clone()
     } else {
-        getcwd().push_many(p.components)
+        let mut ret = getcwd();
+        ret.push_path(p);
+        ret
     }
 }
 
@@ -661,7 +664,7 @@ pub fn make_dir(p: &Path, mode: c_int) -> bool {
         unsafe {
             use os::win32::as_utf16_p;
             // FIXME: turn mode into something useful? #2623
-            do as_utf16_p(p.to_str()) |buf| {
+            do as_utf16_p(p.as_str().unwrap()) |buf| {
                 libc::CreateDirectoryW(buf, ptr::mut_null())
                     != (0 as libc::BOOL)
             }
@@ -690,38 +693,32 @@ pub fn mkdir_recursive(p: &Path, mode: c_int) -> bool {
     if path_is_dir(p) {
         return true;
     }
-    else if p.components.is_empty() {
-        return false;
-    }
-    else if p.components.len() == 1 {
-        // No parent directories to create
-        path_is_dir(p) || make_dir(p, mode)
-    }
-    else {
-        mkdir_recursive(&p.pop(), mode) && make_dir(p, mode)
+    let mut p_ = p.clone();
+    if p_.pop().is_some() {
+        if !mkdir_recursive(&p_, mode) {
+            return false;
+        }
     }
+    return make_dir(p, mode);
 }
 
 /// Lists the contents of a directory
-pub fn list_dir(p: &Path) -> ~[~str] {
-    if p.components.is_empty() && !p.is_absolute() {
-        // Not sure what the right behavior is here, but this
-        // prevents a bounds check failure later
-        return ~[];
-    }
+///
+/// Each resulting Path is a relative path with no directory component.
+pub fn list_dir(p: &Path) -> ~[Path] {
     unsafe {
         #[cfg(target_os = "linux")]
         #[cfg(target_os = "android")]
         #[cfg(target_os = "freebsd")]
         #[cfg(target_os = "macos")]
-        unsafe fn get_list(p: &Path) -> ~[~str] {
+        unsafe fn get_list(p: &Path) -> ~[Path] {
             #[fixed_stack_segment]; #[inline(never)];
             use libc::{dirent_t};
             use libc::{opendir, readdir, closedir};
             extern {
                 fn rust_list_dir_val(ptr: *dirent_t) -> *libc::c_char;
             }
-            let mut strings = ~[];
+            let mut paths = ~[];
             debug2!("os::list_dir -- BEFORE OPENDIR");
 
             let dir_ptr = do p.with_c_str |buf| {
@@ -732,8 +729,8 @@ pub fn list_dir(p: &Path) -> ~[~str] {
                 debug2!("os::list_dir -- opendir() SUCCESS");
                 let mut entry_ptr = readdir(dir_ptr);
                 while (entry_ptr as uint != 0) {
-                    strings.push(str::raw::from_c_str(rust_list_dir_val(
-                        entry_ptr)));
+                    let cstr = CString::new(rust_list_dir_val(entry_ptr), false);
+                    paths.push(GenericPath::from_c_str(cstr));
                     entry_ptr = readdir(dir_ptr);
                 }
                 closedir(dir_ptr);
@@ -741,11 +738,11 @@ pub fn list_dir(p: &Path) -> ~[~str] {
             else {
                 debug2!("os::list_dir -- opendir() FAILURE");
             }
-            debug2!("os::list_dir -- AFTER -- \\#: {}", strings.len());
-            strings
+            debug2!("os::list_dir -- AFTER -- \\#: {}", paths.len());
+            paths
         }
         #[cfg(windows)]
-        unsafe fn get_list(p: &Path) -> ~[~str] {
+        unsafe fn get_list(p: &Path) -> ~[Path] {
             #[fixed_stack_segment]; #[inline(never)];
             use libc::consts::os::extra::INVALID_HANDLE_VALUE;
             use libc::{wcslen, free};
@@ -765,9 +762,9 @@ pub fn list_dir(p: &Path) -> ~[~str] {
                 fn rust_list_dir_wfd_size() -> libc::size_t;
                 fn rust_list_dir_wfd_fp_buf(wfd: *libc::c_void) -> *u16;
             }
-            fn star(p: &Path) -> Path { p.push("*") }
-            do as_utf16_p(star(p).to_str()) |path_ptr| {
-                let mut strings = ~[];
+            let star = p.join_str("*");
+            do as_utf16_p(star.as_str().unwrap()) |path_ptr| {
+                let mut paths = ~[];
                 let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint);
                 let find_handle = FindFirstFileW(path_ptr, wfd_ptr as HANDLE);
                 if find_handle as libc::c_int != INVALID_HANDLE_VALUE {
@@ -781,18 +778,18 @@ pub fn list_dir(p: &Path) -> ~[~str] {
                             let fp_vec = vec::from_buf(
                                 fp_buf, wcslen(fp_buf) as uint);
                             let fp_str = str::from_utf16(fp_vec);
-                            strings.push(fp_str);
+                            paths.push(Path::from_str(fp_str));
                         }
                         more_files = FindNextFileW(find_handle, wfd_ptr as HANDLE);
                     }
                     FindClose(find_handle);
                     free(wfd_ptr)
                 }
-                strings
+                paths
             }
         }
-        do get_list(p).move_iter().filter |filename| {
-            "." != *filename && ".." != *filename
+        do get_list(p).move_iter().filter |path| {
+            path.as_vec() != bytes!(".") && path.as_vec() != bytes!("..")
         }.collect()
     }
 }
@@ -803,7 +800,7 @@ pub fn list_dir(p: &Path) -> ~[~str] {
  * This version prepends each entry with the directory.
  */
 pub fn list_dir_path(p: &Path) -> ~[Path] {
-    list_dir(p).map(|f| p.push(*f))
+    list_dir(p).map(|f| p.join_path(f))
 }
 
 /// Removes a directory at the specified path, after removing
@@ -838,7 +835,7 @@ pub fn remove_dir(p: &Path) -> bool {
         #[fixed_stack_segment]; #[inline(never)];
         unsafe {
             use os::win32::as_utf16_p;
-            return do as_utf16_p(p.to_str()) |buf| {
+            return do as_utf16_p(p.as_str().unwrap()) |buf| {
                 libc::RemoveDirectoryW(buf) != (0 as libc::BOOL)
             };
         }
@@ -865,7 +862,7 @@ pub fn change_dir(p: &Path) -> bool {
         #[fixed_stack_segment]; #[inline(never)];
         unsafe {
             use os::win32::as_utf16_p;
-            return do as_utf16_p(p.to_str()) |buf| {
+            return do as_utf16_p(p.as_str().unwrap()) |buf| {
                 libc::SetCurrentDirectoryW(buf) != (0 as libc::BOOL)
             };
         }
@@ -891,8 +888,8 @@ pub fn copy_file(from: &Path, to: &Path) -> bool {
         #[fixed_stack_segment]; #[inline(never)];
         unsafe {
             use os::win32::as_utf16_p;
-            return do as_utf16_p(from.to_str()) |fromp| {
-                do as_utf16_p(to.to_str()) |top| {
+            return do as_utf16_p(from.as_str().unwrap()) |fromp| {
+                do as_utf16_p(to.as_str().unwrap()) |top| {
                     libc::CopyFileW(fromp, top, (0 as libc::BOOL)) !=
                         (0 as libc::BOOL)
                 }
@@ -968,7 +965,7 @@ pub fn remove_file(p: &Path) -> bool {
         #[fixed_stack_segment]; #[inline(never)];
         unsafe {
             use os::win32::as_utf16_p;
-            return do as_utf16_p(p.to_str()) |buf| {
+            return do as_utf16_p(p.as_str().unwrap()) |buf| {
                 libc::DeleteFileW(buf) != (0 as libc::BOOL)
             };
         }
@@ -1657,35 +1654,45 @@ pub mod consts {
         pub static SYSNAME: &'static str = "macos";
         pub static DLL_PREFIX: &'static str = "lib";
         pub static DLL_SUFFIX: &'static str = ".dylib";
+        pub static DLL_EXTENSION: &'static str = "dylib";
         pub static EXE_SUFFIX: &'static str = "";
+        pub static EXE_EXTENSION: &'static str = "";
     }
 
     pub mod freebsd {
         pub static SYSNAME: &'static str = "freebsd";
         pub static DLL_PREFIX: &'static str = "lib";
         pub static DLL_SUFFIX: &'static str = ".so";
+        pub static DLL_EXTENSION: &'static str = "so";
         pub static EXE_SUFFIX: &'static str = "";
+        pub static EXE_EXTENSION: &'static str = "";
     }
 
     pub mod linux {
         pub static SYSNAME: &'static str = "linux";
         pub static DLL_PREFIX: &'static str = "lib";
         pub static DLL_SUFFIX: &'static str = ".so";
+        pub static DLL_EXTENSION: &'static str = "so";
         pub static EXE_SUFFIX: &'static str = "";
+        pub static EXE_EXTENSION: &'static str = "";
     }
 
     pub mod android {
         pub static SYSNAME: &'static str = "android";
         pub static DLL_PREFIX: &'static str = "lib";
         pub static DLL_SUFFIX: &'static str = ".so";
+        pub static DLL_EXTENSION: &'static str = "so";
         pub static EXE_SUFFIX: &'static str = "";
+        pub static EXE_EXTENSION: &'static str = "";
     }
 
     pub mod win32 {
         pub static SYSNAME: &'static str = "win32";
         pub static DLL_PREFIX: &'static str = "";
         pub static DLL_SUFFIX: &'static str = ".dll";
+        pub static DLL_EXTENSION: &'static str = "dll";
         pub static EXE_SUFFIX: &'static str = ".exe";
+        pub static EXE_EXTENSION: &'static str = "exe";
     }
 
 
@@ -1790,7 +1797,7 @@ mod tests {
         debug2!("{:?}", path.clone());
 
         // Hard to test this function
-        assert!(path.is_absolute);
+        assert!(path.is_absolute());
     }
 
     #[test]
@@ -1823,12 +1830,13 @@ mod tests {
 
     #[test]
     fn test() {
-        assert!((!Path("test-path").is_absolute));
+        assert!((!Path::from_str("test-path").is_absolute()));
 
-        debug2!("Current working directory: {}", getcwd().to_str());
+        let cwd = getcwd();
+        debug2!("Current working directory: {}", cwd.display());
 
-        debug2!("{:?}", make_absolute(&Path("test-path")));
-        debug2!("{:?}", make_absolute(&Path("/usr/bin")));
+        debug2!("{:?}", make_absolute(&Path::from_str("test-path")));
+        debug2!("{:?}", make_absolute(&Path::from_str("/usr/bin")));
     }
 
     #[test]
@@ -1837,7 +1845,7 @@ mod tests {
         let oldhome = getenv("HOME");
 
         setenv("HOME", "/home/MountainView");
-        assert_eq!(os::homedir(), Some(Path("/home/MountainView")));
+        assert_eq!(os::homedir(), Some(Path::from_str("/home/MountainView")));
 
         setenv("HOME", "");
         assert!(os::homedir().is_none());
@@ -1858,16 +1866,16 @@ mod tests {
         assert!(os::homedir().is_none());
 
         setenv("HOME", "/home/MountainView");
-        assert_eq!(os::homedir(), Some(Path("/home/MountainView")));
+        assert_eq!(os::homedir(), Some(Path::from_str("/home/MountainView")));
 
         setenv("HOME", "");
 
         setenv("USERPROFILE", "/home/MountainView");
-        assert_eq!(os::homedir(), Some(Path("/home/MountainView")));
+        assert_eq!(os::homedir(), Some(Path::from_str("/home/MountainView")));
 
         setenv("HOME", "/home/MountainView");
         setenv("USERPROFILE", "/home/PaloAlto");
-        assert_eq!(os::homedir(), Some(Path("/home/MountainView")));
+        assert_eq!(os::homedir(), Some(Path::from_str("/home/MountainView")));
 
         for s in oldhome.iter() { setenv("HOME", *s) }
         for s in olduserprofile.iter() { setenv("USERPROFILE", *s) }
@@ -1875,18 +1883,20 @@ mod tests {
 
     #[test]
     fn tmpdir() {
-        assert!(!os::tmpdir().to_str().is_empty());
+        let p = os::tmpdir();
+        let s = p.as_str();
+        assert!(s.is_some() && s.unwrap() != ".");
     }
 
     // Issue #712
     #[test]
     fn test_list_dir_no_invalid_memory_access() {
-        os::list_dir(&Path("."));
+        os::list_dir(&Path::from_str("."));
     }
 
     #[test]
     fn list_dir() {
-        let dirs = os::list_dir(&Path("."));
+        let dirs = os::list_dir(&Path::from_str("."));
         // Just assuming that we've got some contents in the current directory
         assert!(dirs.len() > 0u);
 
@@ -1896,43 +1906,37 @@ mod tests {
     }
 
     #[test]
-    fn list_dir_empty_path() {
-        let dirs = os::list_dir(&Path(""));
-        assert!(dirs.is_empty());
-    }
-
-    #[test]
     #[cfg(not(windows))]
     fn list_dir_root() {
-        let dirs = os::list_dir(&Path("/"));
+        let dirs = os::list_dir(&Path::from_str("/"));
         assert!(dirs.len() > 1);
     }
     #[test]
     #[cfg(windows)]
     fn list_dir_root() {
-        let dirs = os::list_dir(&Path("C:\\"));
+        let dirs = os::list_dir(&Path::from_str("C:\\"));
         assert!(dirs.len() > 1);
     }
 
 
     #[test]
     fn path_is_dir() {
-        assert!((os::path_is_dir(&Path("."))));
-        assert!((!os::path_is_dir(&Path("test/stdtest/fs.rs"))));
+        assert!((os::path_is_dir(&Path::from_str("."))));
+        assert!((!os::path_is_dir(&Path::from_str("test/stdtest/fs.rs"))));
     }
 
     #[test]
     fn path_exists() {
-        assert!((os::path_exists(&Path("."))));
-        assert!((!os::path_exists(&Path(
+        assert!((os::path_exists(&Path::from_str("."))));
+        assert!((!os::path_exists(&Path::from_str(
                      "test/nonexistent-bogus-path"))));
     }
 
     #[test]
     fn copy_file_does_not_exist() {
-      assert!(!os::copy_file(&Path("test/nonexistent-bogus-path"),
-                            &Path("test/other-bogus-path")));
-      assert!(!os::path_exists(&Path("test/other-bogus-path")));
+      assert!(!os::copy_file(&Path::from_str("test/nonexistent-bogus-path"),
+                            &Path::from_str("test/other-bogus-path")));
+      assert!(!os::path_exists(&Path::from_str("test/other-bogus-path")));
     }
 
     #[test]
@@ -1942,9 +1946,8 @@ mod tests {
         unsafe {
             let tempdir = getcwd(); // would like to use $TMPDIR,
                                     // doesn't seem to work on Linux
-            assert!((tempdir.to_str().len() > 0u));
-            let input = tempdir.push("in.txt");
-            let out = tempdir.push("out.txt");
+            let input = tempdir.join_str("in.txt");
+            let out = tempdir.join_str("out.txt");
 
             /* Write the temp input file */
             let ostream = do input.with_c_str |fromp| {
@@ -1965,10 +1968,12 @@ mod tests {
             let in_mode = input.get_mode();
             let rs = os::copy_file(&input, &out);
             if (!os::path_exists(&input)) {
-                fail2!("{} doesn't exist", input.to_str());
+                fail2!("{} doesn't exist", input.display());
             }
             assert!((rs));
-            let rslt = run::process_status("diff", [input.to_str(), out.to_str()]);
+            // FIXME (#9639): This needs to handle non-utf8 paths
+            let rslt = run::process_status("diff", [input.as_str().unwrap().to_owned(),
+                                                    out.as_str().unwrap().to_owned()]);
             assert_eq!(rslt, 0);
             assert_eq!(out.get_mode(), in_mode);
             assert!((remove_file(&input)));
@@ -1978,17 +1983,11 @@ mod tests {
 
     #[test]
     fn recursive_mkdir_slash() {
-        let path = Path("/");
+        let path = Path::from_str("/");
         assert!(os::mkdir_recursive(&path,  (S_IRUSR | S_IWUSR | S_IXUSR) as i32));
     }
 
     #[test]
-    fn recursive_mkdir_empty() {
-        let path = Path("");
-        assert!(!os::mkdir_recursive(&path, (S_IRUSR | S_IWUSR | S_IXUSR) as i32));
-    }
-
-    #[test]
     fn memory_map_rw() {
         use result::{Ok, Err};
 
@@ -2032,7 +2031,8 @@ mod tests {
            }
         }
 
-        let path = tmpdir().push("mmap_file.tmp");
+        let mut path = tmpdir();
+        path.push_str("mmap_file.tmp");
         let size = MemoryMap::granularity() * 2;
         remove_file(&path);
 
diff --git a/src/libstd/path.rs b/src/libstd/path.rs
deleted file mode 100644
index ea157c6eea6..00000000000
--- a/src/libstd/path.rs
+++ /dev/null
@@ -1,1507 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-/*!
-
-Cross-platform file path handling
-
-*/
-
-#[allow(missing_doc)];
-
-use c_str::ToCStr;
-use c_str;
-use clone::Clone;
-use cmp::Eq;
-use container::Container;
-use iter::{Iterator, range};
-use libc;
-use num;
-use option::{None, Option, Some};
-use str::{OwnedStr, Str, StrSlice, StrVector};
-use to_str::ToStr;
-use ascii::{AsciiCast, AsciiStr};
-use vec::{Vector, OwnedVector, ImmutableVector, OwnedCopyableVector};
-
-#[cfg(windows)]
-pub use Path = self::WindowsPath;
-#[cfg(unix)]
-pub use Path = self::PosixPath;
-
-#[deriving(Clone, Eq)]
-pub struct WindowsPath {
-    host: Option<~str>,
-    device: Option<~str>,
-    is_absolute: bool,
-    components: ~[~str],
-}
-
-pub fn WindowsPath(s: &str) -> WindowsPath {
-    GenericPath::from_str(s)
-}
-
-#[deriving(Clone, Eq)]
-pub struct PosixPath {
-    is_absolute: bool,
-    components: ~[~str],
-}
-
-pub fn PosixPath(s: &str) -> PosixPath {
-    GenericPath::from_str(s)
-}
-
-pub trait GenericPath : Clone + Eq + ToStr {
-    /// Converts a string to a path.
-    fn from_str(&str) -> Self;
-
-    /// Returns the directory component of `self`, as a string.
-    fn dirname(&self) -> ~str {
-        let s = self.dir_path().to_str();
-        match s.len() {
-            0 => ~".",
-            _ => s,
-        }
-    }
-
-    /// Returns the file component of `self`, as a string option.
-    /// Returns None if `self` names a directory.
-    fn filename<'a>(&'a self) -> Option<&'a str> {
-        match self.components().len() {
-            0 => None,
-            n => Some(self.components()[n - 1].as_slice()),
-        }
-    }
-
-    /// Returns the stem of the file component of `self`, as a string option.
-    /// The stem is the slice of a filename starting at 0 and ending just before
-    /// the last '.' in the name.
-    /// Returns None if `self` names a directory.
-    fn filestem<'a>(&'a self) -> Option<&'a str> {
-        match self.filename() {
-            None => None,
-            Some(ref f) => {
-                match f.rfind('.') {
-                    Some(p) => Some(f.slice_to(p)),
-                    None => Some((*f)),
-                }
-            }
-        }
-    }
-
-    /// Returns the type of the file component of `self`, as a string option.
-    /// The file type is the slice of a filename starting just after the last
-    /// '.' in the name and ending at the last index in the filename.
-    /// Returns None if `self` names a directory.
-    fn filetype<'a>(&'a self) -> Option<&'a str> {
-        match self.filename() {
-            None => None,
-            Some(ref f) => {
-                match f.rfind('.') {
-                    Some(p) if p < f.len() => Some(f.slice_from(p)),
-                    _ => None,
-                }
-            }
-        }
-    }
-
-    /// Returns a new path consisting of `self` with the parent directory component replaced
-    /// with the given string.
-    fn with_dirname(&self, (&str)) -> Self;
-
-    /// Returns a new path consisting of `self` with the file component replaced
-    /// with the given string.
-    fn with_filename(&self, (&str)) -> Self;
-
-    /// Returns a new path consisting of `self` with the file stem replaced
-    /// with the given string.
-    fn with_filestem(&self, s: &str) -> Self {
-        match self.filetype() {
-            None => self.with_filename(s),
-            Some(ref t) => self.with_filename(s.to_owned() + *t),
-        }
-    }
-
-    /// Returns a new path consisting of `self` with the file type replaced
-    /// with the given string.
-    fn with_filetype(&self, t: &str) -> Self {
-        match (t.len(), self.filestem()) {
-            (0, None)        => (*self).clone(),
-            (0, Some(ref s)) => self.with_filename(*s),
-            (_, None)        => self.with_filename(format!(".{}", t)),
-            (_, Some(ref s)) => self.with_filename(format!("{}.{}", *s, t)),
-        }
-    }
-
-    /// Returns the directory component of `self`, as a new path.
-    /// If `self` has no parent, returns `self`.
-    fn dir_path(&self) -> Self {
-        match self.components().len() {
-            0 => (*self).clone(),
-            _ => self.pop(),
-        }
-    }
-
-    /// Returns the file component of `self`, as a new path.
-    /// If `self` names a directory, returns the empty path.
-    fn file_path(&self) -> Self;
-
-    /// Returns a new path whose parent directory is `self` and whose
-    /// file component is the given string.
-    fn push(&self, (&str)) -> Self;
-
-    /// Returns a new path consisting of the given path, made relative to `self`.
-    fn push_rel(&self, other: &Self) -> Self {
-        assert!(!other.is_absolute());
-        self.push_many(other.components())
-    }
-
-    /// Returns a new path consisting of the path given by the given vector
-    /// of strings, relative to `self`.
-    fn push_many<S: Str>(&self, (&[S])) -> Self;
-
-    /// Identical to `dir_path` except in the case where `self` has only one
-    /// component. In this case, `pop` returns the empty path.
-    fn pop(&self) -> Self;
-
-    /// The same as `push_rel`, except that the directory argument must not
-    /// contain directory separators in any of its components.
-    fn unsafe_join(&self, (&Self)) -> Self;
-
-    /// On Unix, always returns `false`. On Windows, returns `true` iff `self`'s
-    /// file stem is one of: `con` `aux` `com1` `com2` `com3` `com4`
-    /// `lpt1` `lpt2` `lpt3` `prn` `nul`.
-    fn is_restricted(&self) -> bool;
-
-    /// Returns a new path that names the same file as `self`, without containing
-    /// any '.', '..', or empty components. On Windows, uppercases the drive letter
-    /// as well.
-    fn normalize(&self) -> Self;
-
-    /// Returns `true` if `self` is an absolute path.
-    fn is_absolute(&self) -> bool;
-
-    /// True if `self` is an ancestor of `other`.
-    // See `test_is_ancestor_of` for examples.
-    fn is_ancestor_of(&self, other: &Self) -> bool {
-        debug2!("{} / {} {} {}", self.to_str(), other.to_str(), self.is_absolute(),
-               self.components().len());
-        self == other ||
-            (!other.components().is_empty() &&
-             !(self.components().is_empty() && !self.is_absolute()) &&
-             self.is_ancestor_of(&other.pop()))
-    }
-
-    /// Finds the relative path from one file to another.
-    fn get_relative_to(&self, abs2: (&Self)) -> Self {
-        assert!(self.is_absolute());
-        assert!(abs2.is_absolute());
-        let abs1 = self.normalize();
-        let abs2 = abs2.normalize();
-
-        let split1: &[~str] = abs1.components();
-        let split2: &[~str] = abs2.components();
-        let len1 = split1.len();
-        let len2 = split2.len();
-        assert!(len1 > 0);
-        assert!(len2 > 0);
-
-        let max_common_path = num::min(len1, len2) - 1;
-        let mut start_idx = 0;
-        while start_idx < max_common_path
-            && split1[start_idx] == split2[start_idx] {
-            start_idx += 1;
-        }
-
-        let mut path: ~[~str] = ~[];
-        for _ in range(start_idx, len1 - 1) { path.push(~".."); };
-
-        path.push_all(split2.slice(start_idx, len2 - 1));
-
-        let mut result: Self = GenericPath::from_str(".");
-        if !path.is_empty() {
-            // Without this type hint, the typechecker doesn't seem to like it
-            let p: Self = GenericPath::from_str("");
-            result = p.push_many(path);
-        };
-        result
-    }
-
-
-    /// Returns `true` iff `child` is a suffix of `parent`. See the test
-    /// case for examples.
-    fn is_parent_of(&self, child: &Self) -> bool {
-        if !self.is_absolute() || child.is_absolute()
-            || self.components().len() < child.components().len()
-            || self.components().is_empty() {
-            return false;
-        }
-        let child_components = child.components().len();
-        let parent_components = self.components().len();
-        let to_drop = self.components().len() - child_components;
-        self.components().slice(to_drop, parent_components) == child.components()
-    }
-
-    fn components<'a>(&'a self) -> &'a [~str];
-}
-
-#[cfg(target_os = "linux")]
-#[cfg(target_os = "android")]
-mod stat {
-    #[cfg(target_arch = "x86")]
-    pub mod arch {
-        use libc;
-
-        pub fn default_stat() -> libc::stat {
-            libc::stat {
-                st_dev: 0,
-                __pad1: 0,
-                st_ino: 0,
-                st_mode: 0,
-                st_nlink: 0,
-                st_uid: 0,
-                st_gid: 0,
-                st_rdev: 0,
-                __pad2: 0,
-                st_size: 0,
-                st_blksize: 0,
-                st_blocks: 0,
-                st_atime: 0,
-                st_atime_nsec: 0,
-                st_mtime: 0,
-                st_mtime_nsec: 0,
-                st_ctime: 0,
-                st_ctime_nsec: 0,
-                __unused4: 0,
-                __unused5: 0,
-            }
-        }
-    }
-
-    #[cfg(target_arch = "arm")]
-    pub mod arch {
-        use libc;
-
-        pub fn default_stat() -> libc::stat {
-            libc::stat {
-                st_dev: 0,
-                __pad0: [0, ..4],
-                __st_ino: 0,
-                st_mode: 0,
-                st_nlink: 0,
-                st_uid: 0,
-                st_gid: 0,
-                st_rdev: 0,
-                __pad3: [0, ..4],
-                st_size: 0,
-                st_blksize: 0,
-                st_blocks: 0,
-                st_atime: 0,
-                st_atime_nsec: 0,
-                st_mtime: 0,
-                st_mtime_nsec: 0,
-                st_ctime: 0,
-                st_ctime_nsec: 0,
-                st_ino: 0
-            }
-        }
-    }
-
-    #[cfg(target_arch = "mips")]
-    pub mod arch {
-        use libc;
-
-        pub fn default_stat() -> libc::stat {
-            libc::stat {
-                st_dev: 0,
-                st_pad1: [0, ..3],
-                st_ino: 0,
-                st_mode: 0,
-                st_nlink: 0,
-                st_uid: 0,
-                st_gid: 0,
-                st_rdev: 0,
-                st_pad2: [0, ..2],
-                st_size: 0,
-                st_pad3: 0,
-                st_atime: 0,
-                st_atime_nsec: 0,
-                st_mtime: 0,
-                st_mtime_nsec: 0,
-                st_ctime: 0,
-                st_ctime_nsec: 0,
-                st_blksize: 0,
-                st_blocks: 0,
-                st_pad5: [0, ..14],
-            }
-        }
-    }
-
-    #[cfg(target_arch = "x86_64")]
-    pub mod arch {
-        use libc;
-
-        pub fn default_stat() -> libc::stat {
-            libc::stat {
-                st_dev: 0,
-                st_ino: 0,
-                st_nlink: 0,
-                st_mode: 0,
-                st_uid: 0,
-                st_gid: 0,
-                __pad0: 0,
-                st_rdev: 0,
-                st_size: 0,
-                st_blksize: 0,
-                st_blocks: 0,
-                st_atime: 0,
-                st_atime_nsec: 0,
-                st_mtime: 0,
-                st_mtime_nsec: 0,
-                st_ctime: 0,
-                st_ctime_nsec: 0,
-                __unused: [0, 0, 0],
-            }
-        }
-    }
-}
-
-#[cfg(target_os = "freebsd")]
-mod stat {
-    #[cfg(target_arch = "x86_64")]
-    pub mod arch {
-        use libc;
-
-        pub fn default_stat() -> libc::stat {
-            libc::stat {
-                st_dev: 0,
-                st_ino: 0,
-                st_mode: 0,
-                st_nlink: 0,
-                st_uid: 0,
-                st_gid: 0,
-                st_rdev: 0,
-                st_atime: 0,
-                st_atime_nsec: 0,
-                st_mtime: 0,
-                st_mtime_nsec: 0,
-                st_ctime: 0,
-                st_ctime_nsec: 0,
-                st_size: 0,
-                st_blocks: 0,
-                st_blksize: 0,
-                st_flags: 0,
-                st_gen: 0,
-                st_lspare: 0,
-                st_birthtime: 0,
-                st_birthtime_nsec: 0,
-                __unused: [0, 0],
-            }
-        }
-    }
-}
-
-#[cfg(target_os = "macos")]
-mod stat {
-    pub mod arch {
-        use libc;
-
-        pub fn default_stat() -> libc::stat {
-            libc::stat {
-                st_dev: 0,
-                st_mode: 0,
-                st_nlink: 0,
-                st_ino: 0,
-                st_uid: 0,
-                st_gid: 0,
-                st_rdev: 0,
-                st_atime: 0,
-                st_atime_nsec: 0,
-                st_mtime: 0,
-                st_mtime_nsec: 0,
-                st_ctime: 0,
-                st_ctime_nsec: 0,
-                st_birthtime: 0,
-                st_birthtime_nsec: 0,
-                st_size: 0,
-                st_blocks: 0,
-                st_blksize: 0,
-                st_flags: 0,
-                st_gen: 0,
-                st_lspare: 0,
-                st_qspare: [0, 0],
-            }
-        }
-    }
-}
-
-#[cfg(target_os = "win32")]
-mod stat {
-    pub mod arch {
-        use libc;
-        pub fn default_stat() -> libc::stat {
-            libc::stat {
-                st_dev: 0,
-                st_ino: 0,
-                st_mode: 0,
-                st_nlink: 0,
-                st_uid: 0,
-                st_gid: 0,
-                st_rdev: 0,
-                st_size: 0,
-                st_atime: 0,
-                st_mtime: 0,
-                st_ctime: 0,
-            }
-        }
-    }
-}
-
-#[cfg(target_os = "win32")]
-impl WindowsPath {
-    pub fn stat(&self) -> Option<libc::stat> {
-        #[fixed_stack_segment]; #[inline(never)];
-        do self.with_c_str |buf| {
-            let mut st = stat::arch::default_stat();
-            match unsafe { libc::stat(buf, &mut st) } {
-                0 => Some(st),
-                _ => None,
-            }
-        }
-    }
-
-    pub fn exists(&self) -> bool {
-        match self.stat() {
-            None => false,
-            Some(_) => true,
-        }
-    }
-
-    pub fn get_size(&self) -> Option<i64> {
-        match self.stat() {
-            None => None,
-            Some(ref st) => Some(st.st_size as i64),
-        }
-    }
-
-    pub fn get_mode(&self) -> Option<uint> {
-        match self.stat() {
-            None => None,
-            Some(ref st) => Some(st.st_mode as uint),
-        }
-    }
-}
-
-#[cfg(not(target_os = "win32"))]
-impl PosixPath {
-    pub fn stat(&self) -> Option<libc::stat> {
-        #[fixed_stack_segment]; #[inline(never)];
-        do self.with_c_str |buf| {
-            let mut st = stat::arch::default_stat();
-            match unsafe { libc::stat(buf as *libc::c_char, &mut st) } {
-                0 => Some(st),
-                _ => None,
-            }
-        }
-    }
-
-    pub fn exists(&self) -> bool {
-        match self.stat() {
-            None => false,
-            Some(_) => true,
-        }
-    }
-
-    pub fn get_size(&self) -> Option<i64> {
-        match self.stat() {
-            None => None,
-            Some(ref st) => Some(st.st_size as i64),
-        }
-    }
-
-    pub fn get_mode(&self) -> Option<uint> {
-        match self.stat() {
-            None => None,
-            Some(ref st) => Some(st.st_mode as uint),
-        }
-    }
-
-    /// Executes a function `f` on `self` as well as on all of its ancestors.
-    pub fn each_parent(&self, f: &fn(&Path)) {
-        if !self.components.is_empty() {
-            f(self);
-            self.pop().each_parent(f);
-        }
-    }
-
-}
-
-#[cfg(target_os = "freebsd")]
-#[cfg(target_os = "linux")]
-#[cfg(target_os = "macos")]
-impl PosixPath {
-    pub fn get_atime(&self) -> Option<(i64, int)> {
-        match self.stat() {
-            None => None,
-            Some(ref st) => {
-                Some((st.st_atime as i64,
-                      st.st_atime_nsec as int))
-            }
-        }
-    }
-
-    pub fn get_mtime(&self) -> Option<(i64, int)> {
-        match self.stat() {
-            None => None,
-            Some(ref st) => {
-                Some((st.st_mtime as i64,
-                      st.st_mtime_nsec as int))
-            }
-        }
-    }
-
-    pub fn get_ctime(&self) -> Option<(i64, int)> {
-        match self.stat() {
-            None => None,
-            Some(ref st) => {
-                Some((st.st_ctime as i64,
-                      st.st_ctime_nsec as int))
-            }
-        }
-    }
-}
-
-#[cfg(unix)]
-impl PosixPath {
-    pub fn lstat(&self) -> Option<libc::stat> {
-        #[fixed_stack_segment]; #[inline(never)];
-        do self.with_c_str |buf| {
-            let mut st = stat::arch::default_stat();
-            match unsafe { libc::lstat(buf, &mut st) } {
-                0 => Some(st),
-                _ => None,
-            }
-        }
-    }
-}
-
-#[cfg(target_os = "freebsd")]
-#[cfg(target_os = "macos")]
-impl PosixPath {
-    pub fn get_birthtime(&self) -> Option<(i64, int)> {
-        match self.stat() {
-            None => None,
-            Some(ref st) => {
-                Some((st.st_birthtime as i64,
-                      st.st_birthtime_nsec as int))
-            }
-        }
-    }
-}
-
-#[cfg(target_os = "win32")]
-impl WindowsPath {
-    pub fn get_atime(&self) -> Option<(i64, int)> {
-        match self.stat() {
-            None => None,
-            Some(ref st) => {
-                Some((st.st_atime as i64, 0))
-            }
-        }
-    }
-
-    pub fn get_mtime(&self) -> Option<(i64, int)> {
-        match self.stat() {
-            None => None,
-            Some(ref st) => {
-                Some((st.st_mtime as i64, 0))
-            }
-        }
-    }
-
-    pub fn get_ctime(&self) -> Option<(i64, int)> {
-        match self.stat() {
-            None => None,
-            Some(ref st) => {
-                Some((st.st_ctime as i64, 0))
-            }
-        }
-    }
-
-    /// Executes a function `f` on `self` as well as on all of its ancestors.
-    pub fn each_parent(&self, f: &fn(&Path)) {
-        if !self.components.is_empty() {
-            f(self);
-            self.pop().each_parent(f);
-        }
-    }
-}
-
-impl ToStr for PosixPath {
-    fn to_str(&self) -> ~str {
-        let mut s = ~"";
-        if self.is_absolute {
-            s.push_str("/");
-        }
-        s + self.components.connect("/")
-    }
-}
-
-impl ToCStr for PosixPath {
-    fn to_c_str(&self) -> c_str::CString {
-        self.to_str().to_c_str()
-    }
-
-    unsafe fn to_c_str_unchecked(&self) -> c_str::CString {
-        self.to_str().to_c_str_unchecked()
-    }
-}
-
-impl GenericPath for PosixPath {
-    fn from_str(s: &str) -> PosixPath {
-        let components = s.split_iter('/')
-            .filter_map(|s| if s.is_empty() {None} else {Some(s.to_owned())})
-            .collect();
-        let is_absolute = (s.len() != 0 && s[0] == '/' as u8);
-        PosixPath {
-            is_absolute: is_absolute,
-            components: components,
-        }
-    }
-
-    fn with_dirname(&self, d: &str) -> PosixPath {
-        let dpath = PosixPath(d);
-        match self.filename() {
-            Some(ref f) => dpath.push(*f),
-            None => dpath,
-        }
-    }
-
-    fn with_filename(&self, f: &str) -> PosixPath {
-        assert!(!f.iter().all(posix::is_sep));
-        self.dir_path().push(f)
-    }
-
-    fn file_path(&self) -> PosixPath {
-        let cs = match self.filename() {
-          None => ~[],
-          Some(ref f) => ~[(*f).to_owned()]
-        };
-        PosixPath {
-            is_absolute: false,
-            components: cs,
-        }
-    }
-
-    fn push(&self, s: &str) -> PosixPath {
-        let mut v = self.components.clone();
-        for s in s.split_iter(posix::is_sep) {
-            if !s.is_empty() {
-                v.push(s.to_owned())
-            }
-        }
-        PosixPath {
-            components: v,
-            ..(*self).clone()
-        }
-    }
-
-    fn push_many<S: Str>(&self, cs: &[S]) -> PosixPath {
-        let mut v = self.components.clone();
-        for e in cs.iter() {
-            for s in e.as_slice().split_iter(posix::is_sep) {
-                if !s.is_empty() {
-                    v.push(s.to_owned())
-                }
-            }
-        }
-        PosixPath {
-            is_absolute: self.is_absolute,
-            components: v,
-        }
-    }
-
-    fn pop(&self) -> PosixPath {
-        let mut cs = self.components.clone();
-        if cs.len() != 0 {
-            cs.pop();
-        }
-        PosixPath {
-            is_absolute: self.is_absolute,
-            components: cs,
-        } //..self }
-    }
-
-    fn unsafe_join(&self, other: &PosixPath) -> PosixPath {
-        if other.is_absolute {
-            PosixPath {
-                is_absolute: true,
-                components: other.components.clone(),
-            }
-        } else {
-            self.push_rel(other)
-        }
-    }
-
-    fn is_restricted(&self) -> bool {
-        false
-    }
-
-    fn normalize(&self) -> PosixPath {
-        PosixPath {
-            is_absolute: self.is_absolute,
-            components: normalize(self.components),
-        } // ..self }
-    }
-
-    fn is_absolute(&self) -> bool {
-        self.is_absolute
-    }
-
-    fn components<'a>(&'a self) -> &'a [~str] { self.components.as_slice() }
-
-}
-
-
-impl ToStr for WindowsPath {
-    fn to_str(&self) -> ~str {
-        let mut s = ~"";
-        match self.host {
-          Some(ref h) => {
-            s.push_str("\\\\");
-            s.push_str(*h);
-          }
-          None => { }
-        }
-        match self.device {
-          Some(ref d) => {
-            s.push_str(*d);
-            s.push_str(":");
-          }
-          None => { }
-        }
-        if self.is_absolute {
-            s.push_str("\\");
-        }
-        s + self.components.connect("\\")
-    }
-}
-
-impl c_str::ToCStr for WindowsPath {
-    fn to_c_str(&self) -> c_str::CString {
-        self.to_str().to_c_str()
-    }
-
-    unsafe fn to_c_str_unchecked(&self) -> c_str::CString {
-        self.to_str().to_c_str_unchecked()
-    }
-}
-
-impl GenericPath for WindowsPath {
-    fn from_str(s: &str) -> WindowsPath {
-        let host;
-        let device;
-        let rest;
-
-        match (
-            windows::extract_drive_prefix(s),
-            windows::extract_unc_prefix(s),
-        ) {
-            (Some((ref d, ref r)), _) => {
-                host = None;
-                device = Some((*d).clone());
-                rest = (*r).clone();
-            }
-            (None, Some((ref h, ref r))) => {
-                host = Some((*h).clone());
-                device = None;
-                rest = (*r).clone();
-            }
-            (None, None) => {
-                host = None;
-                device = None;
-                rest = s.to_owned();
-            }
-        }
-
-        let components = rest.split_iter(windows::is_sep)
-            .filter_map(|s| if s.is_empty() {None} else {Some(s.to_owned())})
-            .collect();
-
-        let is_absolute = (rest.len() != 0 && windows::is_sep(rest[0] as char));
-        WindowsPath {
-            host: host,
-            device: device,
-            is_absolute: is_absolute,
-            components: components,
-        }
-    }
-
-    fn with_dirname(&self, d: &str) -> WindowsPath {
-        let dpath = WindowsPath(d);
-        match self.filename() {
-            Some(ref f) => dpath.push(*f),
-            None => dpath,
-        }
-    }
-
-    fn with_filename(&self, f: &str) -> WindowsPath {
-        assert!(! f.iter().all(windows::is_sep));
-        self.dir_path().push(f)
-    }
-
-    fn file_path(&self) -> WindowsPath {
-        WindowsPath {
-            host: None,
-            device: None,
-            is_absolute: false,
-            components: match self.filename() {
-                None => ~[],
-                Some(ref f) => ~[(*f).to_owned()],
-            }
-        }
-    }
-
-    fn push(&self, s: &str) -> WindowsPath {
-        let mut v = self.components.clone();
-        for s in s.split_iter(windows::is_sep) {
-            if !s.is_empty() {
-                v.push(s.to_owned())
-            }
-        }
-        WindowsPath { components: v, ..(*self).clone() }
-    }
-
-    fn push_many<S: Str>(&self, cs: &[S]) -> WindowsPath {
-        let mut v = self.components.clone();
-        for e in cs.iter() {
-            for s in e.as_slice().split_iter(windows::is_sep) {
-                if !s.is_empty() {
-                    v.push(s.to_owned())
-                }
-            }
-        }
-        // tedious, but as-is, we can't use ..self
-        WindowsPath {
-            host: self.host.clone(),
-            device: self.device.clone(),
-            is_absolute: self.is_absolute,
-            components: v
-        }
-    }
-
-    fn pop(&self) -> WindowsPath {
-        let mut cs = self.components.clone();
-        if cs.len() != 0 {
-            cs.pop();
-        }
-        WindowsPath {
-            host: self.host.clone(),
-            device: self.device.clone(),
-            is_absolute: self.is_absolute,
-            components: cs,
-        }
-    }
-
-    fn unsafe_join(&self, other: &WindowsPath) -> WindowsPath {
-        /* rhs not absolute is simple push */
-        if !other.is_absolute {
-            return self.push_many(other.components);
-        }
-
-        /* if rhs has a host set, then the whole thing wins */
-        match other.host {
-            Some(ref host) => {
-                return WindowsPath {
-                    host: Some((*host).clone()),
-                    device: other.device.clone(),
-                    is_absolute: true,
-                    components: other.components.clone(),
-                };
-            }
-            _ => {}
-        }
-
-        /* if rhs has a device set, then a part wins */
-        match other.device {
-            Some(ref device) => {
-                return WindowsPath {
-                    host: None,
-                    device: Some((*device).clone()),
-                    is_absolute: true,
-                    components: other.components.clone(),
-                };
-            }
-            _ => {}
-        }
-
-        /* fallback: host and device of lhs win, but the
-           whole path of the right */
-        WindowsPath {
-            host: self.host.clone(),
-            device: self.device.clone(),
-            is_absolute: self.is_absolute || other.is_absolute,
-            components: other.components.clone(),
-        }
-    }
-
-    fn is_restricted(&self) -> bool {
-        match self.filestem() {
-            Some(stem) => {
-                // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use
-                // to_ascii_move and to_str_move to not do a unnecessary copy.
-                match stem.to_ascii().to_lower().to_str_ascii() {
-                    ~"con" | ~"aux" | ~"com1" | ~"com2" | ~"com3" | ~"com4" |
-                    ~"lpt1" | ~"lpt2" | ~"lpt3" | ~"prn" | ~"nul" => true,
-                    _ => false
-                }
-            },
-            None => false
-        }
-    }
-
-    fn normalize(&self) -> WindowsPath {
-        WindowsPath {
-            host: self.host.clone(),
-            device: match self.device {
-                None => None,
-
-                // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use
-                // to_ascii_move and to_str_move to not do a unnecessary copy.
-                Some(ref device) => Some(device.to_ascii().to_upper().to_str_ascii())
-            },
-            is_absolute: self.is_absolute,
-            components: normalize(self.components)
-        }
-    }
-
-    fn is_absolute(&self) -> bool {
-        self.is_absolute
-    }
-
-    fn components<'a>(&'a self) -> &'a [~str] { self.components.as_slice() }
-
-}
-
-pub fn normalize(components: &[~str]) -> ~[~str] {
-    let mut cs = ~[];
-    for c in components.iter() {
-        if *c == ~"." && components.len() > 1 { continue; }
-        if *c == ~"" { continue; }
-        if *c == ~".." && cs.len() != 0 {
-            cs.pop();
-            continue;
-        }
-        cs.push((*c).clone());
-    }
-    cs
-}
-
-// Various posix helpers.
-pub mod posix {
-
-    #[inline]
-    pub fn is_sep(u: char) -> bool {
-        u == '/'
-    }
-
-}
-
-// Various windows helpers.
-pub mod windows {
-    use libc;
-    use option::{None, Option, Some};
-
-    #[inline]
-    pub fn is_sep(u: char) -> bool {
-        u == '/' || u == '\\'
-    }
-
-    pub fn extract_unc_prefix(s: &str) -> Option<(~str,~str)> {
-        if (s.len() > 1 &&
-            (s[0] == '\\' as u8 || s[0] == '/' as u8) &&
-            s[0] == s[1]) {
-            let mut i = 2;
-            while i < s.len() {
-                if is_sep(s[i] as char) {
-                    let pre = s.slice(2, i).to_owned();
-                    let rest = s.slice(i, s.len()).to_owned();
-                    return Some((pre, rest));
-                }
-                i += 1;
-            }
-        }
-        None
-    }
-
-    pub fn extract_drive_prefix(s: &str) -> Option<(~str,~str)> {
-        #[fixed_stack_segment]; #[inline(never)];
-
-        unsafe {
-            if (s.len() > 1 &&
-                libc::isalpha(s[0] as libc::c_int) != 0 &&
-                s[1] == ':' as u8) {
-                let rest = if s.len() == 2 {
-                    ~""
-                } else {
-                    s.slice(2, s.len()).to_owned()
-                };
-                return Some((s.slice(0,1).to_owned(), rest));
-            }
-            None
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use option::{None, Some};
-    use path::{PosixPath, WindowsPath, windows};
-
-    #[test]
-    fn test_double_slash_collapsing() {
-        let path = PosixPath("tmp/");
-        let path = path.push("/hmm");
-        let path = path.normalize();
-        assert_eq!(~"tmp/hmm", path.to_str());
-
-        let path = WindowsPath("tmp/");
-        let path = path.push("/hmm");
-        let path = path.normalize();
-        assert_eq!(~"tmp\\hmm", path.to_str());
-    }
-
-    #[test]
-    fn test_filetype_foo_bar() {
-        let wp = PosixPath("foo.bar");
-        assert_eq!(wp.filetype(), Some(".bar"));
-
-        let wp = WindowsPath("foo.bar");
-        assert_eq!(wp.filetype(), Some(".bar"));
-    }
-
-    #[test]
-    fn test_filetype_foo() {
-        let wp = PosixPath("foo");
-        assert_eq!(wp.filetype(), None);
-
-        let wp = WindowsPath("foo");
-        assert_eq!(wp.filetype(), None);
-    }
-
-    #[test]
-    fn test_posix_paths() {
-        fn t(wp: &PosixPath, s: &str) {
-            let ss = wp.to_str();
-            let sss = s.to_owned();
-            if (ss != sss) {
-                debug2!("got {}", ss);
-                debug2!("expected {}", sss);
-                assert_eq!(ss, sss);
-            }
-        }
-
-        t(&(PosixPath("hi")), "hi");
-        t(&(PosixPath("/lib")), "/lib");
-        t(&(PosixPath("hi/there")), "hi/there");
-        t(&(PosixPath("hi/there.txt")), "hi/there.txt");
-
-        t(&(PosixPath("hi/there.txt")), "hi/there.txt");
-        t(&(PosixPath("hi/there.txt")
-           .with_filetype("")), "hi/there");
-
-        t(&(PosixPath("/a/b/c/there.txt")
-            .with_dirname("hi")), "hi/there.txt");
-
-        t(&(PosixPath("hi/there.txt")
-            .with_dirname(".")), "./there.txt");
-
-        t(&(PosixPath("a/b/c")
-            .push("..")), "a/b/c/..");
-
-        t(&(PosixPath("there.txt")
-            .with_filetype("o")), "there.o");
-
-        t(&(PosixPath("hi/there.txt")
-            .with_filetype("o")), "hi/there.o");
-
-        t(&(PosixPath("hi/there.txt")
-            .with_filetype("o")
-            .with_dirname("/usr/lib")),
-          "/usr/lib/there.o");
-
-        t(&(PosixPath("hi/there.txt")
-            .with_filetype("o")
-            .with_dirname("/usr/lib/")),
-          "/usr/lib/there.o");
-
-        t(&(PosixPath("hi/there.txt")
-            .with_filetype("o")
-            .with_dirname("/usr//lib//")),
-            "/usr/lib/there.o");
-
-        t(&(PosixPath("/usr/bin/rust")
-            .push_many([~"lib", ~"thingy.so"])
-            .with_filestem("librustc")),
-          "/usr/bin/rust/lib/librustc.so");
-
-    }
-
-    #[test]
-    fn test_posix_push_with_backslash() {
-        let a = PosixPath("/aaa/bbb");
-        let b = a.push("x\\y"); // \ is not a file separator for posix paths
-        assert_eq!(a.components.len(), 2);
-        assert_eq!(b.components.len(), 3);
-    }
-
-    #[test]
-    fn test_normalize() {
-        fn t(wp: &PosixPath, s: &str) {
-            let ss = wp.to_str();
-            let sss = s.to_owned();
-            if (ss != sss) {
-                debug2!("got {}", ss);
-                debug2!("expected {}", sss);
-                assert_eq!(ss, sss);
-            }
-        }
-
-        t(&(PosixPath("hi/there.txt")
-            .with_dirname(".").normalize()), "there.txt");
-
-        t(&(PosixPath("a/b/../c/././/../foo.txt/").normalize()),
-          "a/foo.txt");
-
-        t(&(PosixPath("a/b/c")
-            .push("..").normalize()), "a/b");
-    }
-
-    #[test]
-    fn test_extract_unc_prefixes() {
-        assert!(windows::extract_unc_prefix("\\\\").is_none());
-        assert!(windows::extract_unc_prefix("//").is_none());
-        assert!(windows::extract_unc_prefix("\\\\hi").is_none());
-        assert!(windows::extract_unc_prefix("//hi").is_none());
-        assert!(windows::extract_unc_prefix("\\\\hi\\") ==
-            Some((~"hi", ~"\\")));
-        assert!(windows::extract_unc_prefix("//hi\\") ==
-            Some((~"hi", ~"\\")));
-        assert!(windows::extract_unc_prefix("\\\\hi\\there") ==
-            Some((~"hi", ~"\\there")));
-        assert!(windows::extract_unc_prefix("//hi/there") ==
-            Some((~"hi", ~"/there")));
-        assert!(windows::extract_unc_prefix(
-            "\\\\hi\\there\\friends.txt") ==
-            Some((~"hi", ~"\\there\\friends.txt")));
-        assert!(windows::extract_unc_prefix(
-            "//hi\\there\\friends.txt") ==
-            Some((~"hi", ~"\\there\\friends.txt")));
-    }
-
-    #[test]
-    fn test_extract_drive_prefixes() {
-        assert!(windows::extract_drive_prefix("c").is_none());
-        assert!(windows::extract_drive_prefix("c:") ==
-                     Some((~"c", ~"")));
-        assert!(windows::extract_drive_prefix("d:") ==
-                     Some((~"d", ~"")));
-        assert!(windows::extract_drive_prefix("z:") ==
-                     Some((~"z", ~"")));
-        assert!(windows::extract_drive_prefix("c:\\hi") ==
-                     Some((~"c", ~"\\hi")));
-        assert!(windows::extract_drive_prefix("d:hi") ==
-                     Some((~"d", ~"hi")));
-        assert!(windows::extract_drive_prefix("c:hi\\there.txt") ==
-                     Some((~"c", ~"hi\\there.txt")));
-        assert!(windows::extract_drive_prefix("c:\\hi\\there.txt") ==
-                     Some((~"c", ~"\\hi\\there.txt")));
-    }
-
-    #[test]
-    fn test_windows_paths() {
-        fn t(wp: &WindowsPath, s: &str) {
-            let ss = wp.to_str();
-            let sss = s.to_owned();
-            if (ss != sss) {
-                debug2!("got {}", ss);
-                debug2!("expected {}", sss);
-                assert_eq!(ss, sss);
-            }
-        }
-
-        t(&(WindowsPath("hi")), "hi");
-        t(&(WindowsPath("hi/there")), "hi\\there");
-        t(&(WindowsPath("hi/there.txt")), "hi\\there.txt");
-
-        t(&(WindowsPath("there.txt")
-            .with_filetype("o")), "there.o");
-
-        t(&(WindowsPath("hi/there.txt")
-            .with_filetype("o")), "hi\\there.o");
-
-        t(&(WindowsPath("hi/there.txt")
-            .with_filetype("o")
-            .with_dirname("c:\\program files A")),
-          "c:\\program files A\\there.o");
-
-        t(&(WindowsPath("hi/there.txt")
-            .with_filetype("o")
-            .with_dirname("c:\\program files B\\")),
-          "c:\\program files B\\there.o");
-
-        t(&(WindowsPath("hi/there.txt")
-            .with_filetype("o")
-            .with_dirname("c:\\program files C\\/")),
-            "c:\\program files C\\there.o");
-
-        t(&(WindowsPath("c:\\program files (x86)\\rust")
-            .push_many([~"lib", ~"thingy.dll"])
-            .with_filename("librustc.dll")),
-          "c:\\program files (x86)\\rust\\lib\\librustc.dll");
-
-        t(&(WindowsPath("\\\\computer\\share")
-            .unsafe_join(&WindowsPath("\\a"))),
-          "\\\\computer\\a");
-
-        t(&(WindowsPath("//computer/share")
-            .unsafe_join(&WindowsPath("\\a"))),
-          "\\\\computer\\a");
-
-        t(&(WindowsPath("//computer/share")
-            .unsafe_join(&WindowsPath("\\\\computer\\share"))),
-          "\\\\computer\\share");
-
-        t(&(WindowsPath("C:/whatever")
-            .unsafe_join(&WindowsPath("//computer/share/a/b"))),
-          "\\\\computer\\share\\a\\b");
-
-        t(&(WindowsPath("C:")
-            .unsafe_join(&WindowsPath("D:/foo"))),
-          "D:\\foo");
-
-        t(&(WindowsPath("C:")
-            .unsafe_join(&WindowsPath("B"))),
-          "C:B");
-
-        t(&(WindowsPath("C:")
-            .unsafe_join(&WindowsPath("/foo"))),
-          "C:\\foo");
-
-        t(&(WindowsPath("C:\\")
-            .unsafe_join(&WindowsPath("\\bar"))),
-          "C:\\bar");
-
-        t(&(WindowsPath("")
-            .unsafe_join(&WindowsPath(""))),
-          "");
-
-        t(&(WindowsPath("")
-            .unsafe_join(&WindowsPath("a"))),
-          "a");
-
-        t(&(WindowsPath("")
-            .unsafe_join(&WindowsPath("C:\\a"))),
-          "C:\\a");
-
-        t(&(WindowsPath("c:\\foo")
-            .normalize()),
-          "C:\\foo");
-    }
-
-    #[test]
-    fn test_windows_path_restrictions() {
-        assert_eq!(WindowsPath("hi").is_restricted(), false);
-        assert_eq!(WindowsPath("C:\\NUL").is_restricted(), true);
-        assert_eq!(WindowsPath("C:\\COM1.TXT").is_restricted(), true);
-        assert_eq!(WindowsPath("c:\\prn.exe").is_restricted(), true);
-    }
-
-    #[test]
-    fn test_is_ancestor_of() {
-        assert!(&PosixPath("/a/b").is_ancestor_of(&PosixPath("/a/b/c/d")));
-        assert!(!&PosixPath("/a/b/c/d").is_ancestor_of(&PosixPath("/a/b")));
-        assert!(!&PosixPath("/a/b").is_ancestor_of(&PosixPath("/c/d")));
-        assert!(&PosixPath("/a/b").is_ancestor_of(&PosixPath("/a/b/c/d")));
-        assert!(&PosixPath("/").is_ancestor_of(&PosixPath("/a/b/c")));
-        assert!(!&PosixPath("/").is_ancestor_of(&PosixPath("")));
-        assert!(!&PosixPath("/a/b/c").is_ancestor_of(&PosixPath("")));
-        assert!(!&PosixPath("").is_ancestor_of(&PosixPath("/a/b/c")));
-
-        assert!(&WindowsPath("C:\\a\\b").is_ancestor_of(&WindowsPath("C:\\a\\b\\c\\d")));
-        assert!(!&WindowsPath("C:\\a\\b\\c\\d").is_ancestor_of(&WindowsPath("C:\\a\\b")));
-        assert!(!&WindowsPath("C:\\a\\b").is_ancestor_of(&WindowsPath("C:\\c\\d")));
-        assert!(&WindowsPath("C:\\a\\b").is_ancestor_of(&WindowsPath("C:\\a\\b\\c\\d")));
-        assert!(&WindowsPath("C:\\").is_ancestor_of(&WindowsPath("C:\\a\\b\\c")));
-        assert!(!&WindowsPath("C:\\").is_ancestor_of(&WindowsPath("")));
-        assert!(!&WindowsPath("C:\\a\\b\\c").is_ancestor_of(&WindowsPath("")));
-        assert!(!&WindowsPath("").is_ancestor_of(&WindowsPath("C:\\a\\b\\c")));
-
-    }
-
-    #[test]
-    fn test_relative_to1() {
-        let p1 = PosixPath("/usr/bin/rustc");
-        let p2 = PosixPath("/usr/lib/mylib");
-        let res = p1.get_relative_to(&p2);
-        assert_eq!(res, PosixPath("../lib"));
-
-        let p1 = WindowsPath("C:\\usr\\bin\\rustc");
-        let p2 = WindowsPath("C:\\usr\\lib\\mylib");
-        let res = p1.get_relative_to(&p2);
-        assert_eq!(res, WindowsPath("..\\lib"));
-
-    }
-
-    #[test]
-    fn test_relative_to2() {
-        let p1 = PosixPath("/usr/bin/rustc");
-        let p2 = PosixPath("/usr/bin/../lib/mylib");
-        let res = p1.get_relative_to(&p2);
-        assert_eq!(res, PosixPath("../lib"));
-
-        let p1 = WindowsPath("C:\\usr\\bin\\rustc");
-        let p2 = WindowsPath("C:\\usr\\bin\\..\\lib\\mylib");
-        let res = p1.get_relative_to(&p2);
-        assert_eq!(res, WindowsPath("..\\lib"));
-    }
-
-    #[test]
-    fn test_relative_to3() {
-        let p1 = PosixPath("/usr/bin/whatever/rustc");
-        let p2 = PosixPath("/usr/lib/whatever/mylib");
-        let res = p1.get_relative_to(&p2);
-        assert_eq!(res, PosixPath("../../lib/whatever"));
-
-        let p1 = WindowsPath("C:\\usr\\bin\\whatever\\rustc");
-        let p2 = WindowsPath("C:\\usr\\lib\\whatever\\mylib");
-        let res = p1.get_relative_to(&p2);
-        assert_eq!(res, WindowsPath("..\\..\\lib\\whatever"));
-
-    }
-
-    #[test]
-    fn test_relative_to4() {
-        let p1 = PosixPath("/usr/bin/whatever/../rustc");
-        let p2 = PosixPath("/usr/lib/whatever/mylib");
-        let res = p1.get_relative_to(&p2);
-        assert_eq!(res, PosixPath("../lib/whatever"));
-
-        let p1 = WindowsPath("C:\\usr\\bin\\whatever\\..\\rustc");
-        let p2 = WindowsPath("C:\\usr\\lib\\whatever\\mylib");
-        let res = p1.get_relative_to(&p2);
-        assert_eq!(res, WindowsPath("..\\lib\\whatever"));
-
-    }
-
-    #[test]
-    fn test_relative_to5() {
-        let p1 = PosixPath("/usr/bin/whatever/../rustc");
-        let p2 = PosixPath("/usr/lib/whatever/../mylib");
-        let res = p1.get_relative_to(&p2);
-        assert_eq!(res, PosixPath("../lib"));
-
-        let p1 = WindowsPath("C:\\usr\\bin/whatever\\..\\rustc");
-        let p2 = WindowsPath("C:\\usr\\lib\\whatever\\..\\mylib");
-        let res = p1.get_relative_to(&p2);
-        assert_eq!(res, WindowsPath("..\\lib"));
-    }
-
-    #[test]
-    fn test_relative_to6() {
-        let p1 = PosixPath("/1");
-        let p2 = PosixPath("/2/3");
-        let res = p1.get_relative_to(&p2);
-        assert_eq!(res, PosixPath("2"));
-
-        let p1 = WindowsPath("C:\\1");
-        let p2 = WindowsPath("C:\\2\\3");
-        let res = p1.get_relative_to(&p2);
-        assert_eq!(res, WindowsPath("2"));
-
-    }
-
-    #[test]
-    fn test_relative_to7() {
-        let p1 = PosixPath("/1/2");
-        let p2 = PosixPath("/3");
-        let res = p1.get_relative_to(&p2);
-        assert_eq!(res, PosixPath(".."));
-
-        let p1 = WindowsPath("C:\\1\\2");
-        let p2 = WindowsPath("C:\\3");
-        let res = p1.get_relative_to(&p2);
-        assert_eq!(res, WindowsPath(".."));
-
-    }
-
-    #[test]
-    fn test_relative_to8() {
-        let p1 = PosixPath("/home/brian/Dev/rust/build/").push_rel(
-            &PosixPath("stage2/lib/rustc/i686-unknown-linux-gnu/lib/librustc.so"));
-        let p2 = PosixPath("/home/brian/Dev/rust/build/stage2/bin/..").push_rel(
-            &PosixPath("lib/rustc/i686-unknown-linux-gnu/lib/libstd.so"));
-        let res = p1.get_relative_to(&p2);
-        debug2!("test_relative_to8: {} vs. {}",
-               res.to_str(),
-               PosixPath(".").to_str());
-        assert_eq!(res, PosixPath("."));
-
-        let p1 = WindowsPath("C:\\home\\brian\\Dev\\rust\\build\\").push_rel(
-            &WindowsPath("stage2\\lib\\rustc\\i686-unknown-linux-gnu\\lib\\librustc.so"));
-        let p2 = WindowsPath("\\home\\brian\\Dev\\rust\\build\\stage2\\bin\\..").push_rel(
-            &WindowsPath("lib\\rustc\\i686-unknown-linux-gnu\\lib\\libstd.so"));
-        let res = p1.get_relative_to(&p2);
-        debug2!("test_relative_to8: {} vs. {}",
-               res.to_str(),
-               WindowsPath(".").to_str());
-        assert_eq!(res, WindowsPath("."));
-
-    }
-
-
-    #[test]
-    fn test_is_parent_of() {
-        fn is_parent_of_pp(p: &PosixPath, q: &PosixPath) -> bool {
-            p.is_parent_of(q)
-        }
-
-        assert!(is_parent_of_pp(&PosixPath("/a/b/c/d/e"), &PosixPath("c/d/e")));
-        assert!(!is_parent_of_pp(&PosixPath("a/b/c/d/e"), &PosixPath("c/d/e")));
-        assert!(!is_parent_of_pp(&PosixPath("/a/b/c/d/e"), &PosixPath("/c/d/e")));
-        assert!(!is_parent_of_pp(&PosixPath(""), &PosixPath("")));
-        assert!(!is_parent_of_pp(&PosixPath(""), &PosixPath("a/b/c")));
-        assert!(is_parent_of_pp(&PosixPath("/a/b/c"), &PosixPath("")));
-        assert!(is_parent_of_pp(&PosixPath("/a/b/c"), &PosixPath("a/b/c")));
-        assert!(!is_parent_of_pp(&PosixPath("/a/b/c"), &PosixPath("d/e/f")));
-
-        fn is_parent_of_wp(p: &WindowsPath, q: &WindowsPath) -> bool {
-            p.is_parent_of(q)
-        }
-
-        let abcde = WindowsPath("C:\\a\\b\\c\\d\\e");
-        let rel_abcde = WindowsPath("a\\b\\c\\d\\e");
-        let cde   = WindowsPath("c\\d\\e");
-        let slashcde = WindowsPath("C:\\c\\d\\e");
-        let empty = WindowsPath("");
-        let abc = WindowsPath("C:\\a\\b\\c");
-        let rel_abc = WindowsPath("a\\b\\c");
-        let def = WindowsPath("d\\e\\f");
-
-        assert!(is_parent_of_wp(&abcde, &cde));
-        assert!(!is_parent_of_wp(&rel_abcde, &cde));
-        assert!(!is_parent_of_wp(&abcde, &slashcde));
-        assert!(!is_parent_of_wp(&empty, &empty));
-        assert!(!is_parent_of_wp(&empty, &rel_abc));
-        assert!(is_parent_of_wp(&abc, &empty));
-        assert!(is_parent_of_wp(&abc, &rel_abc));
-        assert!(!is_parent_of_wp(&abc, &def));
-    }
-
-}
diff --git a/src/libstd/path2/mod.rs b/src/libstd/path/mod.rs
index 38a48866e22..db6bfada8c0 100644
--- a/src/libstd/path2/mod.rs
+++ b/src/libstd/path/mod.rs
@@ -19,7 +19,7 @@ use option::{Option, None, Some};
 use str;
 use str::{OwnedStr, Str, StrSlice};
 use vec;
-use vec::{CopyableVector, OwnedCopyableVector, OwnedVector};
+use vec::{CopyableVector, OwnedCopyableVector, OwnedVector, Vector};
 use vec::{ImmutableEqVector, ImmutableVector};
 
 /// Typedef for POSIX file paths.
@@ -37,20 +37,31 @@ pub use Path = self::posix::Path;
 #[cfg(windows)]
 pub use Path = self::windows::Path;
 
-/// Typedef for the POSIX path component iterator.
-/// See `posix::ComponentIter` for more info.
-pub use PosixComponentIter = self::posix::ComponentIter;
-
-/// Typedef for the Windows path component iterator.
-/// See `windows::ComponentIter` for more info.
-pub use WindowsComponentIter = self::windows::ComponentIter;
-
 /// Typedef for the platform-native component iterator
 #[cfg(unix)]
 pub use ComponentIter = self::posix::ComponentIter;
+/// Typedef for the platform-native reverse component iterator
+#[cfg(unix)]
+pub use RevComponentIter = self::posix::RevComponentIter;
 /// Typedef for the platform-native component iterator
 #[cfg(windows)]
 pub use ComponentIter = self::windows::ComponentIter;
+/// Typedef for the platform-native reverse component iterator
+#[cfg(windows)]
+pub use RevComponentIter = self::windows::RevComponentIter;
+
+/// Typedef for the platform-native str component iterator
+#[cfg(unix)]
+pub use StrComponentIter = self::posix::StrComponentIter;
+/// Typedef for the platform-native reverse str component iterator
+#[cfg(unix)]
+pub use RevStrComponentIter = self::posix::RevStrComponentIter;
+/// Typedef for the platform-native str component iterator
+#[cfg(windows)]
+pub use StrComponentIter = self::windows::StrComponentIter;
+/// Typedef for the platform-native reverse str component iterator
+#[cfg(windows)]
+pub use RevStrComponentIter = self::windows::RevStrComponentIter;
 
 pub mod posix;
 pub mod windows;
@@ -128,7 +139,10 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
     #[inline]
     fn from_c_str(path: CString) -> Self {
         // CStrings can't contain NULs
-        unsafe { GenericPathUnsafe::from_vec_unchecked(path.as_bytes()) }
+        let v = path.as_bytes();
+        // v is NUL-terminated. Strip it off
+        let v = v.slice_to(v.len()-1);
+        unsafe { GenericPathUnsafe::from_vec_unchecked(v) }
     }
 
     /// Returns the path as a string, if possible.
@@ -146,7 +160,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
     /// If the path is not UTF-8, invalid sequences will be replaced with the unicode
     /// replacement char. This involves allocation.
     #[inline]
-    fn as_display_str<T>(&self, f: &fn(&str) -> T) -> T {
+    fn with_display_str<T>(&self, f: &fn(&str) -> T) -> T {
         match self.as_str() {
             Some(s) => f(s),
             None => {
@@ -161,29 +175,37 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
     /// If the path is not UTF-8, invalid sequences will be replaced with the unicode
     /// replacement char. This involves allocation.
     ///
-    /// This is similar to `as_display_str()` except it will always allocate a new ~str.
+    /// This is similar to `with_display_str()` except it will always allocate a new ~str.
     fn to_display_str(&self) -> ~str {
-        // FIXME (#9516): Don't decode utf-8 manually here once we have a good way to do it in str
-        // This is a truly horrifically bad implementation, done as a functionality stopgap until
-        // we have a proper utf-8 decoder. I don't really want to write one here.
-        static REPLACEMENT_CHAR: char = '\uFFFD';
-
-        let mut v = self.as_vec();
-        let mut s = str::with_capacity(v.len());
-        while !v.is_empty() {
-            let w = str::utf8_char_width(v[0]);
-            if w == 0u {
-                s.push_char(REPLACEMENT_CHAR);
-                v = v.slice_from(1);
-            } else if v.len() < w || !str::is_utf8(v.slice_to(w)) {
-                s.push_char(REPLACEMENT_CHAR);
-                v = v.slice_from(1);
-            } else {
-                s.push_str(unsafe { ::cast::transmute(v.slice_to(w)) });
-                v = v.slice_from(w);
+        from_utf8_with_replacement(self.as_vec())
+    }
+
+    /// Provides the filename as a string
+    ///
+    /// If the filename is not UTF-8, invalid sequences will be replaced with the unicode
+    /// replacement char. This involves allocation.
+    #[inline]
+    fn with_filename_display_str<T>(&self, f: &fn(Option<&str>) -> T) -> T {
+        match self.filename_str() {
+            s@Some(_) => f(s),
+            None => {
+                let o = self.to_filename_display_str();
+                f(o.map(|s|s.as_slice()))
             }
         }
-        s
+    }
+
+    /// Returns the filename as a string
+    ///
+    /// If the filename is not UTF-8, invalid sequences will be replaced with the unicode
+    /// replacement char. This involves allocation.
+    ///
+    /// This is similar to `to_filename_display_str` except it will always allocate a new ~str.
+    fn to_filename_display_str(&self) -> Option<~str> {
+        match self.filename() {
+            None => None,
+            Some(v) => Some(from_utf8_with_replacement(v))
+        }
     }
 
     /// Returns an object that implements `fmt::Default` for printing paths
@@ -211,51 +233,59 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
         str::from_utf8_slice_opt(self.dirname())
     }
     /// Returns the file component of `self`, as a byte vector.
-    /// If `self` represents the root of the file hierarchy, returns the empty vector.
-    /// If `self` is ".", returns the empty vector.
-    fn filename<'a>(&'a self) -> &'a [u8];
+    /// If `self` represents the root of the file hierarchy, returns None.
+    /// If `self` is "." or "..", returns None.
+    fn filename<'a>(&'a self) -> Option<&'a [u8]>;
     /// Returns the file component of `self`, as a string, if possible.
     /// See `filename` for details.
     #[inline]
     fn filename_str<'a>(&'a self) -> Option<&'a str> {
-        str::from_utf8_slice_opt(self.filename())
+        self.filename().and_then(str::from_utf8_slice_opt)
     }
     /// Returns the stem of the filename of `self`, as a byte vector.
     /// The stem is the portion of the filename just before the last '.'.
     /// If there is no '.', the entire filename is returned.
-    fn filestem<'a>(&'a self) -> &'a [u8] {
-        let name = self.filename();
-        let dot = '.' as u8;
-        match name.rposition_elem(&dot) {
-            None | Some(0) => name,
-            Some(1) if name == bytes!("..") => name,
-            Some(pos) => name.slice_to(pos)
+    fn filestem<'a>(&'a self) -> Option<&'a [u8]> {
+        match self.filename() {
+            None => None,
+            Some(name) => Some({
+                let dot = '.' as u8;
+                match name.rposition_elem(&dot) {
+                    None | Some(0) => name,
+                    Some(1) if name == bytes!("..") => name,
+                    Some(pos) => name.slice_to(pos)
+                }
+            })
         }
     }
     /// Returns the stem of the filename of `self`, as a string, if possible.
     /// See `filestem` for details.
     #[inline]
     fn filestem_str<'a>(&'a self) -> Option<&'a str> {
-        str::from_utf8_slice_opt(self.filestem())
+        self.filestem().and_then(str::from_utf8_slice_opt)
     }
     /// Returns the extension of the filename of `self`, as an optional byte vector.
     /// The extension is the portion of the filename just after the last '.'.
     /// If there is no extension, None is returned.
     /// If the filename ends in '.', the empty vector is returned.
     fn extension<'a>(&'a self) -> Option<&'a [u8]> {
-        let name = self.filename();
-        let dot = '.' as u8;
-        match name.rposition_elem(&dot) {
-            None | Some(0) => None,
-            Some(1) if name == bytes!("..") => None,
-            Some(pos) => Some(name.slice_from(pos+1))
+        match self.filename() {
+            None => None,
+            Some(name) => {
+                let dot = '.' as u8;
+                match name.rposition_elem(&dot) {
+                    None | Some(0) => None,
+                    Some(1) if name == bytes!("..") => None,
+                    Some(pos) => Some(name.slice_from(pos+1))
+                }
+            }
         }
     }
     /// Returns the extension of the filename of `self`, as a string, if possible.
     /// See `extension` for details.
     #[inline]
     fn extension_str<'a>(&'a self) -> Option<&'a str> {
-        self.extension().and_then(|v| str::from_utf8_slice_opt(v))
+        self.extension().and_then(str::from_utf8_slice_opt)
     }
 
     /// Replaces the directory portion of the path with the given byte vector.
@@ -322,27 +352,30 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
     fn set_filestem(&mut self, filestem: &[u8]) {
         // borrowck is being a pain here
         let val = {
-            let name = self.filename();
-            if !name.is_empty() {
-                let dot = '.' as u8;
-                match name.rposition_elem(&dot) {
-                    None | Some(0) => None,
-                    Some(idx) => {
-                        let mut v;
-                        if contains_nul(filestem) {
-                            let filestem = self::null_byte::cond.raise(filestem.to_owned());
-                            assert!(!contains_nul(filestem));
-                            v = vec::with_capacity(filestem.len() + name.len() - idx);
-                            v.push_all(filestem);
-                        } else {
-                            v = vec::with_capacity(filestem.len() + name.len() - idx);
-                            v.push_all(filestem);
+            match self.filename() {
+                None => None,
+                Some(name) => {
+                    let dot = '.' as u8;
+                    match name.rposition_elem(&dot) {
+                        None | Some(0) => None,
+                        Some(idx) => {
+                            let mut v;
+                            if contains_nul(filestem) {
+                                let filestem = self::null_byte::cond.raise(filestem.to_owned());
+                                assert!(!contains_nul(filestem));
+                                v = filestem;
+                                let n = v.len();
+                                v.reserve(n + name.len() - idx);
+                            } else {
+                                v = vec::with_capacity(filestem.len() + name.len() - idx);
+                                v.push_all(filestem);
+                            }
+                            v.push_all(name.slice_from(idx));
+                            Some(v)
                         }
-                        v.push_all(name.slice_from(idx));
-                        Some(v)
                     }
                 }
-            } else { None }
+            }
         };
         match val {
             None => self.set_filename(filestem),
@@ -366,52 +399,56 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
     fn set_extension(&mut self, extension: &[u8]) {
         // borrowck causes problems here too
         let val = {
-            let name = self.filename();
-            if !name.is_empty() {
-                let dot = '.' as u8;
-                match name.rposition_elem(&dot) {
-                    None | Some(0) => {
-                        if extension.is_empty() {
-                            None
-                        } else {
-                            let mut v;
-                            if contains_nul(extension) {
-                                let extension = self::null_byte::cond.raise(extension.to_owned());
-                                assert!(!contains_nul(extension));
-                                v = vec::with_capacity(name.len() + extension.len() + 1);
-                                v.push_all(name);
-                                v.push(dot);
-                                v.push_all(extension);
+            match self.filename() {
+                None => None,
+                Some(name) => {
+                    let dot = '.' as u8;
+                    match name.rposition_elem(&dot) {
+                        None | Some(0) => {
+                            if extension.is_empty() {
+                                None
                             } else {
-                                v = vec::with_capacity(name.len() + extension.len() + 1);
-                                v.push_all(name);
-                                v.push(dot);
-                                v.push_all(extension);
+                                let mut v;
+                                if contains_nul(extension) {
+                                    let ext = extension.to_owned();
+                                    let extension = self::null_byte::cond.raise(ext);
+                                    assert!(!contains_nul(extension));
+                                    v = vec::with_capacity(name.len() + extension.len() + 1);
+                                    v.push_all(name);
+                                    v.push(dot);
+                                    v.push_all(extension);
+                                } else {
+                                    v = vec::with_capacity(name.len() + extension.len() + 1);
+                                    v.push_all(name);
+                                    v.push(dot);
+                                    v.push_all(extension);
+                                }
+                                Some(v)
                             }
-                            Some(v)
                         }
-                    }
-                    Some(idx) => {
-                        if extension.is_empty() {
-                            Some(name.slice_to(idx).to_owned())
-                        } else {
-                            let mut v;
-                            if contains_nul(extension) {
-                                let extension = self::null_byte::cond.raise(extension.to_owned());
-                                assert!(!contains_nul(extension));
-                                v = vec::with_capacity(idx + extension.len() + 1);
-                                v.push_all(name.slice_to(idx+1));
-                                v.push_all(extension);
+                        Some(idx) => {
+                            if extension.is_empty() {
+                                Some(name.slice_to(idx).to_owned())
                             } else {
-                                v = vec::with_capacity(idx + extension.len() + 1);
-                                v.push_all(name.slice_to(idx+1));
-                                v.push_all(extension);
+                                let mut v;
+                                if contains_nul(extension) {
+                                    let ext = extension.to_owned();
+                                    let extension = self::null_byte::cond.raise(ext);
+                                    assert!(!contains_nul(extension));
+                                    v = vec::with_capacity(idx + extension.len() + 1);
+                                    v.push_all(name.slice_to(idx+1));
+                                    v.push_all(extension);
+                                } else {
+                                    v = vec::with_capacity(idx + extension.len() + 1);
+                                    v.push_all(name.slice_to(idx+1));
+                                    v.push_all(extension);
+                                }
+                                Some(v)
                             }
-                            Some(v)
                         }
                     }
                 }
-            } else { None }
+            }
         };
         match val {
             None => (),
@@ -424,6 +461,52 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
     fn set_extension_str(&mut self, extension: &str) {
         self.set_extension(extension.as_bytes())
     }
+    /// Adds the given extension (as a byte vector) to the file.
+    /// This does not remove any existing extension.
+    /// `foo.bar`.add_extension(`baz`) becomes `foo.bar.baz`.
+    /// If `self` has no filename, this is a no-op.
+    /// If the given byte vector is [], this is a no-op.
+    ///
+    /// # Failure
+    ///
+    /// Raises the `null_byte` condition if the extension contains a NUL.
+    fn add_extension(&mut self, extension: &[u8]) {
+        if extension.is_empty() { return; }
+        // appease borrowck
+        let val = {
+            match self.filename() {
+                None => None,
+                Some(name) => {
+                    let mut v;
+                    if contains_nul(extension) {
+                        let ext = extension.to_owned();
+                        let extension = self::null_byte::cond.raise(ext);
+                        assert!(!contains_nul(extension));
+                        v = vec::with_capacity(name.len() + 1 + extension.len());
+                        v.push_all(name);
+                        v.push('.' as u8);
+                        v.push_all(extension);
+                    } else {
+                        v = vec::with_capacity(name.len() + 1 + extension.len());
+                        v.push_all(name);
+                        v.push('.' as u8);
+                        v.push_all(extension);
+                    }
+                    Some(v)
+                }
+            }
+        };
+        match val {
+            None => (),
+            Some(v) => unsafe { self.set_filename_unchecked(v) }
+        }
+    }
+    /// Adds the given extension (as a string) to the file.
+    /// See `add_extension` for details.
+    #[inline]
+    fn add_extension_str(&mut self, extension: &str) {
+        self.add_extension(extension.as_bytes())
+    }
 
     /// Returns a new Path constructed by replacing the dirname with the given byte vector.
     /// See `set_dirname` for details.
@@ -516,12 +599,14 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
     /// If `self` represents the root of the filesystem hierarchy, returns None.
     fn file_path(&self) -> Option<Self> {
         // self.filename() returns a NUL-free vector
-        match self.filename() {
-            [] => None,
-            v => Some(unsafe { GenericPathUnsafe::from_vec_unchecked(v) })
-        }
+        self.filename().map_move(|v| unsafe { GenericPathUnsafe::from_vec_unchecked(v) })
     }
 
+    /// Returns a Path that represents the filesystem root that `self` is rooted in.
+    ///
+    /// If `self` is not absolute, or vol-relative in the case of Windows, this returns None.
+    fn root_path(&self) -> Option<Self>;
+
     /// Pushes a path (as a byte vector) onto `self`.
     /// If the argument represents an absolute path, it replaces `self`.
     ///
@@ -554,6 +639,21 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
     fn push_path(&mut self, path: &Self) {
         self.push(path.as_vec())
     }
+    /// Pushes multiple paths (as byte vectors) onto `self`.
+    /// See `push` for details.
+    #[inline]
+    fn push_many<V: Vector<u8>>(&mut self, paths: &[V]) {
+        for p in paths.iter() {
+            self.push(p.as_slice());
+        }
+    }
+    /// Pushes multiple paths (as strings) onto `self`.
+    #[inline]
+    fn push_many_str<S: Str>(&mut self, paths: &[S]) {
+        for p in paths.iter() {
+            self.push_str(p.as_slice());
+        }
+    }
     /// Pops the last path component off of `self` and returns it.
     /// If `self` represents the root of the file hierarchy, None is returned.
     fn pop(&mut self) -> Option<~[u8]>;
@@ -593,12 +693,36 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
         p.push_path(path);
         p
     }
+    /// Returns a new Path constructed by joining `self` with the given paths (as byte vectors).
+    /// See `join` for details.
+    #[inline]
+    fn join_many<V: Vector<u8>>(&self, paths: &[V]) -> Self {
+        let mut p = self.clone();
+        p.push_many(paths);
+        p
+    }
+    /// Returns a new Path constructed by joining `self` with the given paths (as strings).
+    /// See `join` for details.
+    #[inline]
+    fn join_many_str<S: Str>(&self, paths: &[S]) -> Self {
+        let mut p = self.clone();
+        p.push_many_str(paths);
+        p
+    }
 
     /// Returns whether `self` represents an absolute path.
     /// An absolute path is defined as one that, when joined to another path, will
     /// yield back the same absolute path.
     fn is_absolute(&self) -> bool;
 
+    /// Returns whether `self` represents a relative path.
+    /// Typically this is the inverse of `is_absolute`.
+    /// But for Windows paths, it also means the path is not volume-relative or
+    /// relative to the current working directory.
+    fn is_relative(&self) -> bool {
+        !self.is_absolute()
+    }
+
     /// Returns whether `self` is equal to, or is an ancestor of, the given path.
     /// If both paths are relative, they are compared as though they are relative
     /// to the same parent path.
@@ -707,6 +831,30 @@ fn contains_nul(v: &[u8]) -> bool {
     v.iter().any(|&x| x == 0)
 }
 
+#[inline(always)]
+fn from_utf8_with_replacement(mut v: &[u8]) -> ~str {
+    // FIXME (#9516): Don't decode utf-8 manually here once we have a good way to do it in str
+    // This is a truly horrifically bad implementation, done as a functionality stopgap until
+    // we have a proper utf-8 decoder. I don't really want to write one here.
+    static REPLACEMENT_CHAR: char = '\uFFFD';
+
+    let mut s = str::with_capacity(v.len());
+    while !v.is_empty() {
+        let w = str::utf8_char_width(v[0]);
+        if w == 0u {
+            s.push_char(REPLACEMENT_CHAR);
+            v = v.slice_from(1);
+        } else if v.len() < w || !str::is_utf8(v.slice_to(w)) {
+            s.push_char(REPLACEMENT_CHAR);
+            v = v.slice_from(1);
+        } else {
+            s.push_str(unsafe { ::cast::transmute(v.slice_to(w)) });
+            v = v.slice_from(w);
+        }
+    }
+    s
+}
+
 // FIXME (#9537): libc::stat should derive Default
 #[cfg(target_os = "linux")]
 #[cfg(target_os = "android")]
@@ -927,3 +1075,20 @@ mod stat {
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::{GenericPath, PosixPath, WindowsPath};
+    use c_str::ToCStr;
+
+    #[test]
+    fn test_from_c_str() {
+        let input = "/foo/bar/baz";
+        let path: PosixPath = GenericPath::from_c_str(input.to_c_str());
+        assert_eq!(path.as_vec(), input.as_bytes());
+
+        let input = "\\foo\\bar\\baz";
+        let path: WindowsPath = GenericPath::from_c_str(input.to_c_str());
+        assert_eq!(path.as_str().unwrap(), input);
+    }
+}
diff --git a/src/libstd/path2/posix.rs b/src/libstd/path/posix.rs
index 4aa2eb531e5..481387378fa 100644
--- a/src/libstd/path2/posix.rs
+++ b/src/libstd/path/posix.rs
@@ -15,22 +15,30 @@ use c_str::{CString, ToCStr};
 use clone::Clone;
 use cmp::Eq;
 use from_str::FromStr;
-use iter::{AdditiveIterator, Extendable, Iterator};
+use iter::{AdditiveIterator, Extendable, Iterator, Map};
 use option::{Option, None, Some};
 use str;
 use str::Str;
 use to_bytes::IterBytes;
 use util;
 use vec;
-use vec::CopyableVector;
-use vec::{Vector, VectorVector};
+use vec::{CopyableVector, RSplitIterator, SplitIterator, Vector, VectorVector};
 use super::{GenericPath, GenericPathUnsafe};
 
 #[cfg(not(target_os = "win32"))]
 use libc;
 
-/// Iterator that yields successive components of a Path
-pub type ComponentIter<'self> = vec::SplitIterator<'self, u8>;
+/// Iterator that yields successive components of a Path as &[u8]
+pub type ComponentIter<'self> = SplitIterator<'self, u8>;
+/// Iterator that yields components of a Path in reverse as &[u8]
+pub type RevComponentIter<'self> = RSplitIterator<'self, u8>;
+
+/// Iterator that yields successive components of a Path as Option<&str>
+pub type StrComponentIter<'self> = Map<'self, &'self [u8], Option<&'self str>,
+                                       ComponentIter<'self>>;
+/// Iterator that yields components of a Path in reverse as Option<&str>
+pub type RevStrComponentIter<'self> = Map<'self, &'self [u8], Option<&'self str>,
+                                          RevComponentIter<'self>>;
 
 /// Represents a POSIX file path
 #[deriving(Clone, DeepClone)]
@@ -187,12 +195,13 @@ impl GenericPath for Path {
         }
     }
 
-    fn filename<'a>(&'a self) -> &'a [u8] {
+    fn filename<'a>(&'a self) -> Option<&'a [u8]> {
         match self.sepidx {
-            None if bytes!(".") == self.repr || bytes!("..") == self.repr => &[],
-            None => self.repr.as_slice(),
-            Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => &[],
-            Some(idx) => self.repr.slice_from(idx+1)
+            None if bytes!(".") == self.repr || bytes!("..") == self.repr => None,
+            None => Some(self.repr.as_slice()),
+            Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => None,
+            Some(0) if self.repr.slice_from(1).is_empty() => None,
+            Some(idx) => Some(self.repr.slice_from(idx+1))
         }
     }
 
@@ -219,6 +228,14 @@ impl GenericPath for Path {
         }
     }
 
+    fn root_path(&self) -> Option<Path> {
+        if self.is_absolute() {
+            Some(Path::from_str("/"))
+        } else {
+            None
+        }
+    }
+
     #[inline]
     fn is_absolute(&self) -> bool {
         self.repr[0] == sep
@@ -384,6 +401,48 @@ impl Path {
         }
         ret
     }
+
+    /// Returns an iterator that yields each component of the path in reverse.
+    /// See component_iter() for details.
+    pub fn rev_component_iter<'a>(&'a self) -> RevComponentIter<'a> {
+        let v = if self.repr[0] == sep {
+            self.repr.slice_from(1)
+        } else { self.repr.as_slice() };
+        let mut ret = v.rsplit_iter(is_sep);
+        if v.is_empty() {
+            // consume the empty "" component
+            ret.next();
+        }
+        ret
+    }
+
+    /// Returns an iterator that yields each component of the path as Option<&str>.
+    /// See component_iter() for details.
+    pub fn str_component_iter<'a>(&'a self) -> StrComponentIter<'a> {
+        self.component_iter().map(str::from_utf8_slice_opt)
+    }
+
+    /// Returns an iterator that yields each component of the path in reverse as Option<&str>.
+    /// See component_iter() for details.
+    pub fn rev_str_component_iter<'a>(&'a self) -> RevStrComponentIter<'a> {
+        self.rev_component_iter().map(str::from_utf8_slice_opt)
+    }
+
+    /// Returns whether the relative path `child` is a suffix of `self`.
+    pub fn ends_with_path(&self, child: &Path) -> bool {
+        if !child.is_relative() { return false; }
+        let mut selfit = self.rev_component_iter();
+        let mut childit = child.rev_component_iter();
+        loop {
+            match (selfit.next(), childit.next()) {
+                (Some(a), Some(b)) => if a != b { return false; },
+                (Some(_), None) => break,
+                (None, Some(_)) => return false,
+                (None, None) => break
+            }
+        }
+        true
+    }
 }
 
 // None result means the byte vector didn't need normalizing
@@ -422,8 +481,8 @@ fn contains_nul(v: &[u8]) -> bool {
     v.iter().any(|&x| x == 0)
 }
 
-static dot_static: &'static [u8] = &'static ['.' as u8];
-static dot_dot_static: &'static [u8] = &'static ['.' as u8, '.' as u8];
+static dot_static: &'static [u8] = bytes!(".");
+static dot_dot_static: &'static [u8] = bytes!("..");
 
 // Stat support
 #[cfg(not(target_os = "win32"))]
@@ -524,7 +583,7 @@ impl Path {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use option::{Some, None};
+    use option::{Option, Some, None};
     use iter::Iterator;
     use str;
     use vec::Vector;
@@ -604,7 +663,7 @@ mod tests {
 
     #[test]
     fn test_null_byte() {
-        use path2::null_byte::cond;
+        use path::null_byte::cond;
 
         let mut handled = false;
         let mut p = do cond.trap(|v| {
@@ -653,7 +712,7 @@ mod tests {
 
     #[test]
     fn test_null_byte_fail() {
-        use path2::null_byte::cond;
+        use path::null_byte::cond;
         use task;
 
         macro_rules! t(
@@ -709,25 +768,47 @@ mod tests {
         assert_eq!(Path::from_str("foo").to_display_str(), ~"foo");
         assert_eq!(Path::from_vec(b!("foo", 0x80)).to_display_str(), ~"foo\uFFFD");
         assert_eq!(Path::from_vec(b!("foo", 0xff, "bar")).to_display_str(), ~"foo\uFFFDbar");
+        assert_eq!(Path::from_vec(b!("foo", 0xff, "/bar")).to_filename_display_str(), Some(~"bar"));
+        assert_eq!(Path::from_vec(b!("foo/", 0xff, "bar")).to_filename_display_str(),
+                   Some(~"\uFFFDbar"));
+        assert_eq!(Path::from_vec(b!("/")).to_filename_display_str(), None);
 
         let mut called = false;
-        do Path::from_str("foo").as_display_str |s| {
+        do Path::from_str("foo").with_display_str |s| {
             assert_eq!(s, "foo");
             called = true;
         };
         assert!(called);
         called = false;
-        do Path::from_vec(b!("foo", 0x80)).as_display_str |s| {
+        do Path::from_vec(b!("foo", 0x80)).with_display_str |s| {
             assert_eq!(s, "foo\uFFFD");
             called = true;
         };
         assert!(called);
         called = false;
-        do Path::from_vec(b!("foo", 0xff, "bar")).as_display_str |s| {
+        do Path::from_vec(b!("foo", 0xff, "bar")).with_display_str |s| {
             assert_eq!(s, "foo\uFFFDbar");
             called = true;
         };
         assert!(called);
+        called = false;
+        do Path::from_vec(b!("foo", 0xff, "/bar")).with_filename_display_str |s| {
+            assert_eq!(s, Some("bar"));
+            called = true;
+        }
+        assert!(called);
+        called = false;
+        do Path::from_vec(b!("foo/", 0xff, "bar")).with_filename_display_str |s| {
+            assert_eq!(s, Some("\uFFFDbar"));
+            called = true;
+        }
+        assert!(called);
+        called = false;
+        do Path::from_vec(b!("/")).with_filename_display_str |s| {
+            assert!(s.is_none());
+            called = true;
+        }
+        assert!(called);
     }
 
     #[test]
@@ -774,20 +855,20 @@ mod tests {
                     let path = Path::from_vec($path);
                     assert_eq!(path.$op(), $exp);
                 }
-            )
+            );
         )
 
-        t!(v: b!("a/b/c"), filename, b!("c"));
-        t!(v: b!("a/b/c", 0xff), filename, b!("c", 0xff));
-        t!(v: b!("a/b", 0xff, "/c"), filename, b!("c"));
-        t!(s: "a/b/c", filename, "c");
-        t!(s: "/a/b/c", filename, "c");
-        t!(s: "a", filename, "a");
-        t!(s: "/a", filename, "a");
-        t!(s: ".", filename, "");
-        t!(s: "/", filename, "");
-        t!(s: "..", filename, "");
-        t!(s: "../..", filename, "");
+        t!(v: b!("a/b/c"), filename, Some(b!("c")));
+        t!(v: b!("a/b/c", 0xff), filename, Some(b!("c", 0xff)));
+        t!(v: b!("a/b", 0xff, "/c"), filename, Some(b!("c")));
+        t!(s: "a/b/c", filename, Some("c"), opt);
+        t!(s: "/a/b/c", filename, Some("c"), opt);
+        t!(s: "a", filename, Some("a"), opt);
+        t!(s: "/a", filename, Some("a"), opt);
+        t!(s: ".", filename, None, opt);
+        t!(s: "/", filename, None, opt);
+        t!(s: "..", filename, None, opt);
+        t!(s: "../..", filename, None, opt);
 
         t!(v: b!("a/b/c"), dirname, b!("a/b"));
         t!(v: b!("a/b/c", 0xff), dirname, b!("a/b"));
@@ -801,21 +882,21 @@ mod tests {
         t!(s: "..", dirname, "..");
         t!(s: "../..", dirname, "../..");
 
-        t!(v: b!("hi/there.txt"), filestem, b!("there"));
-        t!(v: b!("hi/there", 0x80, ".txt"), filestem, b!("there", 0x80));
-        t!(v: b!("hi/there.t", 0x80, "xt"), filestem, b!("there"));
-        t!(s: "hi/there.txt", filestem, "there");
-        t!(s: "hi/there", filestem, "there");
-        t!(s: "there.txt", filestem, "there");
-        t!(s: "there", filestem, "there");
-        t!(s: ".", filestem, "");
-        t!(s: "/", filestem, "");
-        t!(s: "foo/.bar", filestem, ".bar");
-        t!(s: ".bar", filestem, ".bar");
-        t!(s: "..bar", filestem, ".");
-        t!(s: "hi/there..txt", filestem, "there.");
-        t!(s: "..", filestem, "");
-        t!(s: "../..", filestem, "");
+        t!(v: b!("hi/there.txt"), filestem, Some(b!("there")));
+        t!(v: b!("hi/there", 0x80, ".txt"), filestem, Some(b!("there", 0x80)));
+        t!(v: b!("hi/there.t", 0x80, "xt"), filestem, Some(b!("there")));
+        t!(s: "hi/there.txt", filestem, Some("there"), opt);
+        t!(s: "hi/there", filestem, Some("there"), opt);
+        t!(s: "there.txt", filestem, Some("there"), opt);
+        t!(s: "there", filestem, Some("there"), opt);
+        t!(s: ".", filestem, None, opt);
+        t!(s: "/", filestem, None, opt);
+        t!(s: "foo/.bar", filestem, Some(".bar"), opt);
+        t!(s: ".bar", filestem, Some(".bar"), opt);
+        t!(s: "..bar", filestem, Some("."), opt);
+        t!(s: "hi/there..txt", filestem, Some("there."), opt);
+        t!(s: "..", filestem, None, opt);
+        t!(s: "../..", filestem, None, opt);
 
         t!(v: b!("hi/there.txt"), extension, Some(b!("txt")));
         t!(v: b!("hi/there", 0x80, ".txt"), extension, Some(b!("txt")));
@@ -879,6 +960,39 @@ mod tests {
     }
 
     #[test]
+    fn test_push_many() {
+        use to_man = at_vec::to_managed_move;
+
+        macro_rules! t(
+            (s: $path:expr, $push:expr, $exp:expr) => (
+                {
+                    let mut p = Path::from_str($path);
+                    p.push_many_str($push);
+                    assert_eq!(p.as_str(), Some($exp));
+                }
+            );
+            (v: $path:expr, $push:expr, $exp:expr) => (
+                {
+                    let mut p = Path::from_vec($path);
+                    p.push_many($push);
+                    assert_eq!(p.as_vec(), $exp);
+                }
+            )
+        )
+
+        t!(s: "a/b/c", ["d", "e"], "a/b/c/d/e");
+        t!(s: "a/b/c", ["d", "/e"], "/e");
+        t!(s: "a/b/c", ["d", "/e", "f"], "/e/f");
+        t!(s: "a/b/c", [~"d", ~"e"], "a/b/c/d/e");
+        t!(s: "a/b/c", [@"d", @"e"], "a/b/c/d/e");
+        t!(v: b!("a/b/c"), [b!("d"), b!("e")], b!("a/b/c/d/e"));
+        t!(v: b!("a/b/c"), [b!("d"), b!("/e"), b!("f")], b!("/e/f"));
+        t!(v: b!("a/b/c"), [b!("d").to_owned(), b!("e").to_owned()], b!("a/b/c/d/e"));
+        t!(v: b!("a/b/c"), [to_man(b!("d").to_owned()), to_man(b!("e").to_owned())],
+              b!("a/b/c/d/e"));
+    }
+
+    #[test]
     fn test_pop() {
         macro_rules! t(
             (s: $path:expr, $left:expr, $right:expr) => (
@@ -927,6 +1041,12 @@ mod tests {
     }
 
     #[test]
+    fn test_root_path() {
+        assert_eq!(Path::from_vec(b!("a/b/c")).root_path(), None);
+        assert_eq!(Path::from_vec(b!("/a/b/c")).root_path(), Some(Path::from_str("/")));
+    }
+
+    #[test]
     fn test_join() {
         t!(v: Path::from_vec(b!("a/b/c")).join(b!("..")), b!("a/b"));
         t!(v: Path::from_vec(b!("/a/b/c")).join(b!("d")), b!("/a/b/c/d"));
@@ -961,6 +1081,38 @@ mod tests {
     }
 
     #[test]
+    fn test_join_many() {
+        use to_man = at_vec::to_managed_move;
+
+        macro_rules! t(
+            (s: $path:expr, $join:expr, $exp:expr) => (
+                {
+                    let path = Path::from_str($path);
+                    let res = path.join_many_str($join);
+                    assert_eq!(res.as_str(), Some($exp));
+                }
+            );
+            (v: $path:expr, $join:expr, $exp:expr) => (
+                {
+                    let path = Path::from_vec($path);
+                    let res = path.join_many($join);
+                    assert_eq!(res.as_vec(), $exp);
+                }
+            )
+        )
+
+        t!(s: "a/b/c", ["d", "e"], "a/b/c/d/e");
+        t!(s: "a/b/c", ["..", "d"], "a/b/d");
+        t!(s: "a/b/c", ["d", "/e", "f"], "/e/f");
+        t!(s: "a/b/c", [~"d", ~"e"], "a/b/c/d/e");
+        t!(s: "a/b/c", [@"d", @"e"], "a/b/c/d/e");
+        t!(v: b!("a/b/c"), [b!("d"), b!("e")], b!("a/b/c/d/e"));
+        t!(v: b!("a/b/c"), [b!("d").to_owned(), b!("e").to_owned()], b!("a/b/c/d/e"));
+        t!(v: b!("a/b/c"), [to_man(b!("d").to_owned()), to_man(b!("e").to_owned())],
+              b!("a/b/c/d/e"));
+    }
+
+    #[test]
     fn test_with_helpers() {
         t!(v: Path::from_vec(b!("a/b/c")).with_dirname(b!("d")), b!("d/c"));
         t!(v: Path::from_vec(b!("a/b/c")).with_dirname(b!("d/e")), b!("d/e/c"));
@@ -1122,15 +1274,57 @@ mod tests {
     }
 
     #[test]
+    fn test_add_extension() {
+        macro_rules! t(
+            (s: $path:expr, $ext:expr, $exp:expr) => (
+                {
+                    let mut path = Path::from_str($path);
+                    path.add_extension_str($ext);
+                    assert_eq!(path.as_str(), Some($exp));
+                }
+            );
+            (v: $path:expr, $ext:expr, $exp:expr) => (
+                {
+                    let mut path = Path::from_vec($path);
+                    path.add_extension($ext);
+                    assert_eq!(path.as_vec(), $exp);
+                }
+            )
+        )
+
+        t!(s: "hi/there.txt", "foo", "hi/there.txt.foo");
+        t!(s: "hi/there.", "foo", "hi/there..foo");
+        t!(s: "hi/there", ".foo", "hi/there..foo");
+        t!(s: "hi/there.txt", "", "hi/there.txt");
+        t!(v: b!("hi/there.txt"), b!("foo"), b!("hi/there.txt.foo"));
+        t!(v: b!("hi/there"), b!("bar"), b!("hi/there.bar"));
+        t!(v: b!("/"), b!("foo"), b!("/"));
+        t!(v: b!("."), b!("foo"), b!("."));
+        t!(v: b!("hi/there.", 0x80, "foo"), b!(0xff), b!("hi/there.", 0x80, "foo.", 0xff));
+    }
+
+    #[test]
     fn test_getters() {
         macro_rules! t(
             (s: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => (
                 {
                     let path = $path;
-                    assert_eq!(path.filename_str(), $filename);
-                    assert_eq!(path.dirname_str(), $dirname);
-                    assert_eq!(path.filestem_str(), $filestem);
-                    assert_eq!(path.extension_str(), $ext);
+                    let filename = $filename;
+                    assert!(path.filename_str() == filename,
+                            "`%s`.filename_str(): Expected `%?`, found `%?`",
+                            path.as_str().unwrap(), filename, path.filename_str());
+                    let dirname = $dirname;
+                    assert!(path.dirname_str() == dirname,
+                            "`%s`.dirname_str(): Expected `%?`, found `%?`",
+                            path.as_str().unwrap(), dirname, path.dirname_str());
+                    let filestem = $filestem;
+                    assert!(path.filestem_str() == filestem,
+                            "`%s`.filestem_str(): Expected `%?`, found `%?`",
+                            path.as_str().unwrap(), filestem, path.filestem_str());
+                    let ext = $ext;
+                    assert!(path.extension_str() == ext,
+                            "`%s`.extension_str(): Expected `%?`, found `%?`",
+                            path.as_str().unwrap(), ext, path.extension_str());
                 }
             );
             (v: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => (
@@ -1144,15 +1338,15 @@ mod tests {
             )
         )
 
-        t!(v: Path::from_vec(b!("a/b/c")), b!("c"), b!("a/b"), b!("c"), None);
-        t!(v: Path::from_vec(b!("a/b/", 0xff)), b!(0xff), b!("a/b"), b!(0xff), None);
-        t!(v: Path::from_vec(b!("hi/there.", 0xff)), b!("there.", 0xff), b!("hi"),
-              b!("there"), Some(b!(0xff)));
+        t!(v: Path::from_vec(b!("a/b/c")), Some(b!("c")), b!("a/b"), Some(b!("c")), None);
+        t!(v: Path::from_vec(b!("a/b/", 0xff)), Some(b!(0xff)), b!("a/b"), Some(b!(0xff)), None);
+        t!(v: Path::from_vec(b!("hi/there.", 0xff)), Some(b!("there.", 0xff)), b!("hi"),
+              Some(b!("there")), Some(b!(0xff)));
         t!(s: Path::from_str("a/b/c"), Some("c"), Some("a/b"), Some("c"), None);
-        t!(s: Path::from_str("."), Some(""), Some("."), Some(""), None);
-        t!(s: Path::from_str("/"), Some(""), Some("/"), Some(""), None);
-        t!(s: Path::from_str(".."), Some(""), Some(".."), Some(""), None);
-        t!(s: Path::from_str("../.."), Some(""), Some("../.."), Some(""), None);
+        t!(s: Path::from_str("."), None, Some("."), None, None);
+        t!(s: Path::from_str("/"), None, Some("/"), None, None);
+        t!(s: Path::from_str(".."), None, Some(".."), None, None);
+        t!(s: Path::from_str("../.."), None, Some("../.."), None, None);
         t!(s: Path::from_str("hi/there.txt"), Some("there.txt"), Some("hi"),
               Some("there"), Some("txt"));
         t!(s: Path::from_str("hi/there"), Some("there"), Some("hi"), Some("there"), None);
@@ -1207,14 +1401,23 @@ mod tests {
 
     #[test]
     fn test_is_absolute() {
-        assert_eq!(Path::from_str("a/b/c").is_absolute(), false);
-        assert_eq!(Path::from_str("/a/b/c").is_absolute(), true);
-        assert_eq!(Path::from_str("a").is_absolute(), false);
-        assert_eq!(Path::from_str("/a").is_absolute(), true);
-        assert_eq!(Path::from_str(".").is_absolute(), false);
-        assert_eq!(Path::from_str("/").is_absolute(), true);
-        assert_eq!(Path::from_str("..").is_absolute(), false);
-        assert_eq!(Path::from_str("../..").is_absolute(), false);
+        macro_rules! t(
+            (s: $path:expr, $abs:expr, $rel:expr) => (
+                {
+                    let path = Path::from_str($path);
+                    assert_eq!(path.is_absolute(), $abs);
+                    assert_eq!(path.is_relative(), $rel);
+                }
+            )
+        )
+        t!(s: "a/b/c", false, true);
+        t!(s: "/a/b/c", true, false);
+        t!(s: "a", false, true);
+        t!(s: "/a", true, false);
+        t!(s: ".", false, true);
+        t!(s: "/", true, false);
+        t!(s: "..", false, true);
+        t!(s: "../..", false, true);
     }
 
     #[test]
@@ -1252,6 +1455,45 @@ mod tests {
     }
 
     #[test]
+    fn test_ends_with_path() {
+        macro_rules! t(
+            (s: $path:expr, $child:expr, $exp:expr) => (
+                {
+                    let path = Path::from_str($path);
+                    let child = Path::from_str($child);
+                    assert_eq!(path.ends_with_path(&child), $exp);
+                }
+            );
+            (v: $path:expr, $child:expr, $exp:expr) => (
+                {
+                    let path = Path::from_vec($path);
+                    let child = Path::from_vec($child);
+                    assert_eq!(path.ends_with_path(&child), $exp);
+                }
+            )
+        )
+
+        t!(s: "a/b/c", "c", true);
+        t!(s: "a/b/c", "d", false);
+        t!(s: "foo/bar/quux", "bar", false);
+        t!(s: "foo/bar/quux", "barquux", false);
+        t!(s: "a/b/c", "b/c", true);
+        t!(s: "a/b/c", "a/b/c", true);
+        t!(s: "a/b/c", "foo/a/b/c", false);
+        t!(s: "/a/b/c", "a/b/c", true);
+        t!(s: "/a/b/c", "/a/b/c", false); // child must be relative
+        t!(s: "/a/b/c", "foo/a/b/c", false);
+        t!(s: "a/b/c", "", false);
+        t!(s: "", "", true);
+        t!(s: "/a/b/c", "d/e/f", false);
+        t!(s: "a/b/c", "a/b", false);
+        t!(s: "a/b/c", "b", false);
+        t!(v: b!("a/b/c"), b!("b/c"), true);
+        t!(v: b!("a/b/", 0xff), b!(0xff), true);
+        t!(v: b!("a/b/", 0xff), b!("b/", 0xff), true);
+    }
+
+    #[test]
     fn test_path_relative_from() {
         macro_rules! t(
             (s: $path:expr, $other:expr, $exp:expr) => (
@@ -1305,7 +1547,12 @@ mod tests {
                     let comps = path.component_iter().to_owned_vec();
                     let exp: &[&str] = $exp;
                     let exps = exp.iter().map(|x| x.as_bytes()).to_owned_vec();
-                    assert_eq!(comps, exps);
+                    assert!(comps == exps, "component_iter: Expected %?, found %?",
+                            comps, exps);
+                    let comps = path.rev_component_iter().to_owned_vec();
+                    let exps = exps.move_rev_iter().to_owned_vec();
+                    assert!(comps == exps, "rev_component_iter: Expected %?, found %?",
+                            comps, exps);
                 }
             );
             (v: [$($arg:expr),+], [$([$($exp:expr),*]),*]) => (
@@ -1313,7 +1560,12 @@ mod tests {
                     let path = Path::from_vec(b!($($arg),+));
                     let comps = path.component_iter().to_owned_vec();
                     let exp: &[&[u8]] = [$(b!($($exp),*)),*];
-                    assert_eq!(comps.as_slice(), exp);
+                    assert!(comps.as_slice() == exp, "component_iter: Expected %?, found %?",
+                            comps.as_slice(), exp);
+                    let comps = path.rev_component_iter().to_owned_vec();
+                    let exp = exp.rev_iter().map(|&x|x).to_owned_vec();
+                    assert!(comps.as_slice() == exp, "rev_component_iter: Expected %?, found %?",
+                            comps.as_slice(), exp);
                 }
             )
         )
@@ -1335,6 +1587,32 @@ mod tests {
     }
 
     #[test]
+    fn test_str_component_iter() {
+        macro_rules! t(
+            (v: [$($arg:expr),+], $exp:expr) => (
+                {
+                    let path = Path::from_vec(b!($($arg),+));
+                    let comps = path.str_component_iter().to_owned_vec();
+                    let exp: &[Option<&str>] = $exp;
+                    assert!(comps.as_slice() == exp, "str_component_iter: Expected %?, found %?",
+                            comps.as_slice(), exp);
+                    let comps = path.rev_str_component_iter().to_owned_vec();
+                    let exp = exp.rev_iter().map(|&x|x).to_owned_vec();
+                    assert!(comps.as_slice() == exp,
+                            "rev_str_component_iter: Expected %?, found %?",
+                            comps.as_slice(), exp);
+                }
+            )
+        )
+
+        t!(v: ["a/b/c"], [Some("a"), Some("b"), Some("c")]);
+        t!(v: ["/", 0xff, "/a/", 0x80], [None, Some("a"), None]);
+        t!(v: ["../../foo", 0xcd, "bar"], [Some(".."), Some(".."), None]);
+        // str_component_iter is a wrapper around component_iter, so no need to do
+        // the full set of tests
+    }
+
+    #[test]
     fn test_each_parent() {
         assert!(Path::from_str("/foo/bar").each_parent(|_| true));
         assert!(!Path::from_str("/foo/bar").each_parent(|_| false));
diff --git a/src/libstd/path2/windows.rs b/src/libstd/path/windows.rs
index 7cc7a5610bd..50cbe698d04 100644
--- a/src/libstd/path2/windows.rs
+++ b/src/libstd/path/windows.rs
@@ -15,10 +15,10 @@ use c_str::{CString, ToCStr};
 use cast;
 use cmp::Eq;
 use from_str::FromStr;
-use iter::{AdditiveIterator, Extendable, Iterator};
+use iter::{AdditiveIterator, DoubleEndedIterator, Extendable, Invert, Iterator, Map};
 use option::{Option, Some, None};
 use str;
-use str::{OwnedStr, Str, StrVector};
+use str::{CharSplitIterator, OwnedStr, Str, StrVector};
 use to_bytes::IterBytes;
 use util;
 use vec::Vector;
@@ -27,8 +27,25 @@ use super::{GenericPath, GenericPathUnsafe};
 #[cfg(target_os = "win32")]
 use libc;
 
-/// Iterator that yields successive components of a Path
-pub type ComponentIter<'self> = str::CharSplitIterator<'self, char>;
+/// Iterator that yields successive components of a Path as &str
+///
+/// Each component is yielded as Option<&str> for compatibility with PosixPath, but
+/// every component in WindowsPath is guaranteed to be Some.
+pub type StrComponentIter<'self> = Map<'self, &'self str, Option<&'self str>,
+                                       CharSplitIterator<'self, char>>;
+/// Iterator that yields components of a Path in reverse as &str
+///
+/// Each component is yielded as Option<&str> for compatibility with PosixPath, but
+/// every component in WindowsPath is guaranteed to be Some.
+pub type RevStrComponentIter<'self> = Invert<Map<'self, &'self str, Option<&'self str>,
+                                                 CharSplitIterator<'self, char>>>;
+
+/// Iterator that yields successive components of a Path as &[u8]
+pub type ComponentIter<'self> = Map<'self, Option<&'self str>, &'self [u8],
+                                    StrComponentIter<'self>>;
+/// Iterator that yields components of a Path in reverse as &[u8]
+pub type RevComponentIter<'self> = Map<'self, Option<&'self str>, &'self [u8],
+                                       RevStrComponentIter<'self>>;
 
 /// Represents a Windows path
 // Notes for Windows path impl:
@@ -361,7 +378,7 @@ impl GenericPath for Path {
     }
 
     #[inline]
-    fn as_display_str<T>(&self, f: &fn(&str) -> T) -> T {
+    fn with_display_str<T>(&self, f: &fn(&str) -> T) -> T {
         f(self.repr.as_slice())
     }
 
@@ -400,33 +417,34 @@ impl GenericPath for Path {
     }
 
     #[inline]
-    fn filename<'a>(&'a self) -> &'a [u8] {
-        self.filename_str().unwrap().as_bytes()
+    fn filename<'a>(&'a self) -> Option<&'a [u8]> {
+        self.filename_str().map_move(|x| x.as_bytes())
     }
 
     /// See `GenericPath::filename_str` for info.
-    /// Always returns a `Some` value.
+    /// Always returns a `Some` value if `filename` returns a `Some` value.
     fn filename_str<'a>(&'a self) -> Option<&'a str> {
-        Some(match self.sepidx_or_prefix_len() {
-            None if "." == self.repr || ".." == self.repr => "",
-            None => self.repr.as_slice(),
-            Some((_,idxa,end)) if self.repr.slice(idxa, end) == ".." => "",
-            Some((_,idxa,end)) => self.repr.slice(idxa, end)
-        })
+        match self.sepidx_or_prefix_len() {
+            None if "." == self.repr || ".." == self.repr => None,
+            None => Some(self.repr.as_slice()),
+            Some((_,idxa,end)) if self.repr.slice(idxa, end) == ".." => None,
+            Some((_,idxa,end)) if idxa == end => None,
+            Some((_,idxa,end)) => Some(self.repr.slice(idxa, end))
+        }
     }
 
     /// See `GenericPath::filestem_str` for info.
-    /// Always returns a `Some` value.
+    /// Always returns a `Some` value if `filestem` returns a `Some` value.
     #[inline]
     fn filestem_str<'a>(&'a self) -> Option<&'a str> {
         // filestem() returns a byte vector that's guaranteed valid UTF-8
-        Some(unsafe { cast::transmute(self.filestem()) })
+        self.filestem().map_move(cast::transmute)
     }
 
     #[inline]
     fn extension_str<'a>(&'a self) -> Option<&'a str> {
         // extension() returns a byte vector that's guaranteed valid UTF-8
-        self.extension().map_move(|v| unsafe { cast::transmute(v) })
+        self.extension().map_move(cast::transmute)
     }
 
     fn dir_path(&self) -> Path {
@@ -434,10 +452,7 @@ impl GenericPath for Path {
     }
 
     fn file_path(&self) -> Option<Path> {
-        match self.filename_str() {
-            None | Some("") => None,
-            Some(s) => Some(unsafe { GenericPathUnsafe::from_str_unchecked(s) })
-        }
+        self.filename_str().map_move(|s| unsafe { GenericPathUnsafe::from_str_unchecked(s) })
     }
 
     #[inline]
@@ -477,6 +492,21 @@ impl GenericPath for Path {
         }
     }
 
+    fn root_path(&self) -> Option<Path> {
+        if self.is_absolute() {
+            Some(Path::from_str(match self.prefix {
+                Some(VerbatimDiskPrefix)|Some(DiskPrefix) => {
+                    self.repr.slice_to(self.prefix_len()+1)
+                }
+                _ => self.repr.slice_to(self.prefix_len())
+            }))
+        } else if self.is_vol_relative() {
+            Some(Path::from_str(self.repr.slice_to(1)))
+        } else {
+            None
+        }
+    }
+
     /// See `GenericPath::is_absolute` for info.
     ///
     /// A Windows Path is considered absolute only if it has a non-volume prefix,
@@ -498,6 +528,11 @@ impl GenericPath for Path {
         }
     }
 
+    #[inline]
+    fn is_relative(&self) -> bool {
+        self.prefix.is_none() && !self.is_vol_relative()
+    }
+
     fn is_ancestor_of(&self, other: &Path) -> bool {
         if !self.equiv_prefix(other) {
             false
@@ -505,8 +540,8 @@ impl GenericPath for Path {
                   self.is_vol_relative() != other.is_vol_relative() {
             false
         } else {
-            let mut ita = self.component_iter();
-            let mut itb = other.component_iter();
+            let mut ita = self.str_component_iter().map(|x|x.unwrap());
+            let mut itb = other.str_component_iter().map(|x|x.unwrap());
             if "." == self.repr {
                 return itb.next() != Some("..");
             }
@@ -553,8 +588,8 @@ impl GenericPath for Path {
                 None
             }
         } else {
-            let mut ita = self.component_iter();
-            let mut itb = base.component_iter();
+            let mut ita = self.str_component_iter().map(|x|x.unwrap());
+            let mut itb = base.str_component_iter().map(|x|x.unwrap());
             let mut comps = ~[];
 
             let a_verb = self.is_verbatim();
@@ -669,13 +704,14 @@ impl Path {
         path
     }
 
-    /// Returns an iterator that yields each component of the path in turn.
+    /// Returns an iterator that yields each component of the path in turn as a Option<&str>.
+    /// Every component is guaranteed to be Some.
     /// Does not yield the path prefix (including server/share components in UNC paths).
     /// Does not distinguish between volume-relative and relative paths, e.g.
     /// \a\b\c and a\b\c.
     /// Does not distinguish between absolute and cwd-relative paths, e.g.
     /// C:\foo and C:foo.
-    pub fn component_iter<'a>(&'a self) -> ComponentIter<'a> {
+    pub fn str_component_iter<'a>(&'a self) -> StrComponentIter<'a> {
         let s = match self.prefix {
             Some(_) => {
                 let plen = self.prefix_len();
@@ -686,10 +722,52 @@ impl Path {
             None if self.repr[0] == sep as u8 => self.repr.slice_from(1),
             None => self.repr.as_slice()
         };
-        let ret = s.split_terminator_iter(sep);
+        let ret = s.split_terminator_iter(sep).map(Some);
         ret
     }
 
+    /// Returns an iterator that yields each component of the path in reverse as an Option<&str>
+    /// See str_component_iter() for details.
+    pub fn rev_str_component_iter<'a>(&'a self) -> RevStrComponentIter<'a> {
+        self.str_component_iter().invert()
+    }
+
+    /// Returns an iterator that yields each component of the path in turn as a &[u8].
+    /// See str_component_iter() for details.
+    pub fn component_iter<'a>(&'a self) -> ComponentIter<'a> {
+        fn convert<'a>(x: Option<&'a str>) -> &'a [u8] {
+            #[inline];
+            x.unwrap().as_bytes()
+        }
+        self.str_component_iter().map(convert)
+    }
+
+    /// Returns an iterator that yields each component of the path in reverse as a &[u8].
+    /// See str_component_iter() for details.
+    pub fn rev_component_iter<'a>(&'a self) -> RevComponentIter<'a> {
+        fn convert<'a>(x: Option<&'a str>) -> &'a [u8] {
+            #[inline];
+            x.unwrap().as_bytes()
+        }
+        self.rev_str_component_iter().map(convert)
+    }
+
+    /// Returns whether the relative path `child` is a suffix of `self`.
+    pub fn ends_with_path(&self, child: &Path) -> bool {
+        if !child.is_relative() { return false; }
+        let mut selfit = self.str_component_iter().invert();
+        let mut childit = child.str_component_iter().invert();
+        loop {
+            match (selfit.next(), childit.next()) {
+                (Some(a), Some(b)) => if a != b { return false; },
+                (Some(_), None) => break,
+                (None, Some(_)) => return false,
+                (None, None) => break
+            }
+        }
+        true
+    }
+
     /// Returns whether the path is considered "volume-relative", which means a path
     /// that looks like "\foo". Paths of this form are relative to the current volume,
     /// but absolute within that volume.
@@ -1096,8 +1174,8 @@ impl Path {
     /// Returns whether the represented file exists
     pub fn exists(&self) -> bool {
         match self.stat() {
-            None => None,
-            Some(st) => Some(st.st_size as i64)
+            None => false,
+            Some(_) => true
         }
     }
 
@@ -1339,7 +1417,7 @@ mod tests {
 
     #[test]
     fn test_null_byte() {
-        use path2::null_byte::cond;
+        use path::null_byte::cond;
 
         let mut handled = false;
         let mut p = do cond.trap(|v| {
@@ -1388,7 +1466,7 @@ mod tests {
 
     #[test]
     fn test_null_byte_fail() {
-        use path2::null_byte::cond;
+        use path::null_byte::cond;
         use task;
 
         macro_rules! t(
@@ -1448,13 +1526,20 @@ mod tests {
     #[test]
     fn test_display_str() {
         assert_eq!(Path::from_str("foo").to_display_str(), ~"foo");
+        assert_eq!(Path::from_vec(b!("\\")).to_filename_display_str(), None);
 
         let mut called = false;
-        do Path::from_str("foo").as_display_str |s| {
+        do Path::from_str("foo").with_display_str |s| {
             assert_eq!(s, "foo");
             called = true;
         };
         assert!(called);
+        called = false;
+        do Path::from_vec(b!("\\")).with_filename_display_str |s| {
+            assert!(s.is_none());
+            called = true;
+        }
+        assert!(called);
     }
 
     #[test]
@@ -1500,37 +1585,37 @@ mod tests {
             )
         )
 
-        t!(v: b!("a\\b\\c"), filename, b!("c"));
+        t!(v: b!("a\\b\\c"), filename, Some(b!("c")));
         t!(s: "a\\b\\c", filename_str, "c");
         t!(s: "\\a\\b\\c", filename_str, "c");
         t!(s: "a", filename_str, "a");
         t!(s: "\\a", filename_str, "a");
-        t!(s: ".", filename_str, "");
-        t!(s: "\\", filename_str, "");
-        t!(s: "..", filename_str, "");
-        t!(s: "..\\..", filename_str, "");
+        t!(s: ".", filename_str, None, opt);
+        t!(s: "\\", filename_str, None, opt);
+        t!(s: "..", filename_str, None, opt);
+        t!(s: "..\\..", filename_str, None, opt);
         t!(s: "c:\\foo.txt", filename_str, "foo.txt");
-        t!(s: "C:\\", filename_str, "");
-        t!(s: "C:", filename_str, "");
+        t!(s: "C:\\", filename_str, None, opt);
+        t!(s: "C:", filename_str, None, opt);
         t!(s: "\\\\server\\share\\foo.txt", filename_str, "foo.txt");
-        t!(s: "\\\\server\\share", filename_str, "");
+        t!(s: "\\\\server\\share", filename_str, None, opt);
         t!(s: "\\\\server", filename_str, "server");
         t!(s: "\\\\?\\bar\\foo.txt", filename_str, "foo.txt");
-        t!(s: "\\\\?\\bar", filename_str, "");
-        t!(s: "\\\\?\\", filename_str, "");
+        t!(s: "\\\\?\\bar", filename_str, None, opt);
+        t!(s: "\\\\?\\", filename_str, None, opt);
         t!(s: "\\\\?\\UNC\\server\\share\\foo.txt", filename_str, "foo.txt");
-        t!(s: "\\\\?\\UNC\\server", filename_str, "");
-        t!(s: "\\\\?\\UNC\\", filename_str, "");
+        t!(s: "\\\\?\\UNC\\server", filename_str, None, opt);
+        t!(s: "\\\\?\\UNC\\", filename_str, None, opt);
         t!(s: "\\\\?\\C:\\foo.txt", filename_str, "foo.txt");
-        t!(s: "\\\\?\\C:\\", filename_str, "");
-        t!(s: "\\\\?\\C:", filename_str, "");
-        t!(s: "\\\\?\\foo/bar", filename_str, "");
-        t!(s: "\\\\?\\C:/foo", filename_str, "");
+        t!(s: "\\\\?\\C:\\", filename_str, None, opt);
+        t!(s: "\\\\?\\C:", filename_str, None, opt);
+        t!(s: "\\\\?\\foo/bar", filename_str, None, opt);
+        t!(s: "\\\\?\\C:/foo", filename_str, None, opt);
         t!(s: "\\\\.\\foo\\bar", filename_str, "bar");
-        t!(s: "\\\\.\\foo", filename_str, "");
-        t!(s: "\\\\.\\foo/bar", filename_str, "");
+        t!(s: "\\\\.\\foo", filename_str, None, opt);
+        t!(s: "\\\\.\\foo/bar", filename_str, None, opt);
         t!(s: "\\\\.\\foo\\bar/baz", filename_str, "bar/baz");
-        t!(s: "\\\\.\\", filename_str, "");
+        t!(s: "\\\\.\\", filename_str, None, opt);
         t!(s: "\\\\?\\a\\b\\", filename_str, "b");
 
         t!(v: b!("a\\b\\c"), dirname, b!("a\\b"));
@@ -1564,19 +1649,19 @@ mod tests {
         t!(s: "\\\\.\\foo", dirname_str, "\\\\.\\foo");
         t!(s: "\\\\?\\a\\b\\", dirname_str, "\\\\?\\a");
 
-        t!(v: b!("hi\\there.txt"), filestem, b!("there"));
+        t!(v: b!("hi\\there.txt"), filestem, Some(b!("there")));
         t!(s: "hi\\there.txt", filestem_str, "there");
         t!(s: "hi\\there", filestem_str, "there");
         t!(s: "there.txt", filestem_str, "there");
         t!(s: "there", filestem_str, "there");
-        t!(s: ".", filestem_str, "");
-        t!(s: "\\", filestem_str, "");
+        t!(s: ".", filestem_str, None, opt);
+        t!(s: "\\", filestem_str, None, opt);
         t!(s: "foo\\.bar", filestem_str, ".bar");
         t!(s: ".bar", filestem_str, ".bar");
         t!(s: "..bar", filestem_str, ".");
         t!(s: "hi\\there..txt", filestem_str, "there.");
-        t!(s: "..", filestem_str, "");
-        t!(s: "..\\..", filestem_str, "");
+        t!(s: "..", filestem_str, None, opt);
+        t!(s: "..\\..", filestem_str, None, opt);
         // filestem is based on filename, so we don't need the full set of prefix tests
 
         t!(v: b!("hi\\there.txt"), extension, Some(b!("txt")));
@@ -1682,6 +1767,39 @@ mod tests {
     }
 
     #[test]
+    fn test_push_many() {
+        use to_man = at_vec::to_managed_move;
+
+        macro_rules! t(
+            (s: $path:expr, $push:expr, $exp:expr) => (
+                {
+                    let mut p = Path::from_str($path);
+                    p.push_many_str($push);
+                    assert_eq!(p.as_str(), Some($exp));
+                }
+            );
+            (v: $path:expr, $push:expr, $exp:expr) => (
+                {
+                    let mut p = Path::from_vec($path);
+                    p.push_many($push);
+                    assert_eq!(p.as_vec(), $exp);
+                }
+            )
+        )
+
+        t!(s: "a\\b\\c", ["d", "e"], "a\\b\\c\\d\\e");
+        t!(s: "a\\b\\c", ["d", "\\e"], "\\e");
+        t!(s: "a\\b\\c", ["d", "\\e", "f"], "\\e\\f");
+        t!(s: "a\\b\\c", [~"d", ~"e"], "a\\b\\c\\d\\e");
+        t!(s: "a\\b\\c", [@"d", @"e"], "a\\b\\c\\d\\e");
+        t!(v: b!("a\\b\\c"), [b!("d"), b!("e")], b!("a\\b\\c\\d\\e"));
+        t!(v: b!("a\\b\\c"), [b!("d"), b!("\\e"), b!("f")], b!("\\e\\f"));
+        t!(v: b!("a\\b\\c"), [b!("d").to_owned(), b!("e").to_owned()], b!("a\\b\\c\\d\\e"));
+        t!(v: b!("a\\b\\c"), [to_man(b!("d").to_owned()), to_man(b!("e").to_owned())],
+              b!("a\\b\\c\\d\\e"));
+    }
+
+    #[test]
     fn test_pop() {
         macro_rules! t(
             (s: $path:expr, $left:expr, $right:expr) => (
@@ -1754,6 +1872,20 @@ mod tests {
     }
 
     #[test]
+    fn test_root_path() {
+        assert_eq!(Path::from_str("a\\b\\c").root_path(), None);
+        assert_eq!(Path::from_str("\\a\\b\\c").root_path(), Some(Path::from_str("\\")));
+        assert_eq!(Path::from_str("C:a").root_path(), None);
+        assert_eq!(Path::from_str("C:\\a").root_path(), Some(Path::from_str("C:\\")));
+        assert_eq!(Path::from_str("\\\\a\\b\\c").root_path(), Some(Path::from_str("\\\\a\\b")));
+        assert_eq!(Path::from_str("\\\\?\\a\\b").root_path(), Some(Path::from_str("\\\\?\\a")));
+        assert_eq!(Path::from_str("\\\\?\\C:\\a").root_path(), Some(Path::from_str("\\\\?\\C:\\")));
+        assert_eq!(Path::from_str("\\\\?\\UNC\\a\\b\\c").root_path(),
+                   Some(Path::from_str("\\\\?\\UNC\\a\\b")));
+        assert_eq!(Path::from_str("\\\\.\\a\\b").root_path(), Some(Path::from_str("\\\\.\\a")));
+    }
+
+    #[test]
     fn test_join() {
         t!(s: Path::from_str("a\\b\\c").join_str(".."), "a\\b");
         t!(s: Path::from_str("\\a\\b\\c").join_str("d"), "\\a\\b\\c\\d");
@@ -1791,6 +1923,38 @@ mod tests {
     }
 
     #[test]
+    fn test_join_many() {
+        use to_man = at_vec::to_managed_move;
+
+        macro_rules! t(
+            (s: $path:expr, $join:expr, $exp:expr) => (
+                {
+                    let path = Path::from_str($path);
+                    let res = path.join_many_str($join);
+                    assert_eq!(res.as_str(), Some($exp));
+                }
+            );
+            (v: $path:expr, $join:expr, $exp:expr) => (
+                {
+                    let path = Path::from_vec($path);
+                    let res = path.join_many($join);
+                    assert_eq!(res.as_vec(), $exp);
+                }
+            )
+        )
+
+        t!(s: "a\\b\\c", ["d", "e"], "a\\b\\c\\d\\e");
+        t!(s: "a\\b\\c", ["..", "d"], "a\\b\\d");
+        t!(s: "a\\b\\c", ["d", "\\e", "f"], "\\e\\f");
+        t!(s: "a\\b\\c", [~"d", ~"e"], "a\\b\\c\\d\\e");
+        t!(s: "a\\b\\c", [@"d", @"e"], "a\\b\\c\\d\\e");
+        t!(v: b!("a\\b\\c"), [b!("d"), b!("e")], b!("a\\b\\c\\d\\e"));
+        t!(v: b!("a\\b\\c"), [b!("d").to_owned(), b!("e").to_owned()], b!("a\\b\\c\\d\\e"));
+        t!(v: b!("a\\b\\c"), [to_man(b!("d").to_owned()), to_man(b!("e").to_owned())],
+              b!("a\\b\\c\\d\\e"));
+    }
+
+    #[test]
     fn test_with_helpers() {
         macro_rules! t(
             (s: $path:expr, $op:ident, $arg:expr, $res:expr) => (
@@ -2003,15 +2167,54 @@ mod tests {
     }
 
     #[test]
+    fn test_add_extension() {
+        macro_rules! t(
+            (s: $path:expr, $ext:expr, $exp:expr) => (
+                {
+                    let mut path = Path::from_str($path);
+                    path.add_extension_str($ext);
+                    assert_eq!(path.as_str(), Some($exp));
+                }
+            );
+            (v: $path:expr, $ext:expr, $exp:expr) => (
+                {
+                    let mut path = Path::from_vec($path);
+                    path.add_extension($ext);
+                    assert_eq!(path.as_vec(), $exp);
+                }
+            )
+        )
+
+        t!(v: b!("hi\\there.txt"), b!("foo"), b!("hi\\there.txt.foo"));
+        t!(v: b!("hi\\there"), b!("bar"), b!("hi\\there.bar"));
+        t!(v: b!("\\"), b!("foo"), b!("\\"));
+        t!(v: b!("."), b!("foo"), b!("."));
+        t!(s: "hi\\there.", "foo", "hi\\there..foo");
+        t!(s: "hi\\there.txt", "", "hi\\there.txt");
+    }
+
+    #[test]
     fn test_getters() {
         macro_rules! t(
             (s: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => (
                 {
                     let path = $path;
-                    assert_eq!(path.filename_str(), $filename);
-                    assert_eq!(path.dirname_str(), $dirname);
-                    assert_eq!(path.filestem_str(), $filestem);
-                    assert_eq!(path.extension_str(), $ext);
+                    let filename = $filename;
+                    assert!(path.filename_str() == filename,
+                            "`%s`.filename_str(): Expected `%?`, found `%?`",
+                            path.as_str().unwrap(), filename, path.filename_str());
+                    let dirname = $dirname;
+                    assert!(path.dirname_str() == dirname,
+                            "`%s`.dirname_str(): Expected `%?`, found `%?`",
+                            path.as_str().unwrap(), dirname, path.dirname_str());
+                    let filestem = $filestem;
+                    assert!(path.filestem_str() == filestem,
+                            "`%s`.filestem_str(): Expected `%?`, found `%?`",
+                            path.as_str().unwrap(), filestem, path.filestem_str());
+                    let ext = $ext;
+                    assert!(path.extension_str() == ext,
+                            "`%s`.extension_str(): Expected `%?`, found `%?`",
+                            path.as_str().unwrap(), ext, path.extension_str());
                 }
             );
             (v: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => (
@@ -2025,12 +2228,12 @@ mod tests {
             )
         )
 
-        t!(v: Path::from_vec(b!("a\\b\\c")), b!("c"), b!("a\\b"), b!("c"), None);
+        t!(v: Path::from_vec(b!("a\\b\\c")), Some(b!("c")), b!("a\\b"), Some(b!("c")), None);
         t!(s: Path::from_str("a\\b\\c"), Some("c"), Some("a\\b"), Some("c"), None);
-        t!(s: Path::from_str("."), Some(""), Some("."), Some(""), None);
-        t!(s: Path::from_str("\\"), Some(""), Some("\\"), Some(""), None);
-        t!(s: Path::from_str(".."), Some(""), Some(".."), Some(""), None);
-        t!(s: Path::from_str("..\\.."), Some(""), Some("..\\.."), Some(""), None);
+        t!(s: Path::from_str("."), None, Some("."), None, None);
+        t!(s: Path::from_str("\\"), None, Some("\\"), None, None);
+        t!(s: Path::from_str(".."), None, Some(".."), None, None);
+        t!(s: Path::from_str("..\\.."), None, Some("..\\.."), None, None);
         t!(s: Path::from_str("hi\\there.txt"), Some("there.txt"), Some("hi"),
               Some("there"), Some("txt"));
         t!(s: Path::from_str("hi\\there"), Some("there"), Some("hi"), Some("there"), None);
@@ -2076,10 +2279,10 @@ mod tests {
     #[test]
     fn test_is_absolute() {
         macro_rules! t(
-            ($path:expr, $abs:expr, $vol:expr, $cwd:expr) => (
+            ($path:expr, $abs:expr, $vol:expr, $cwd:expr, $rel:expr) => (
                 {
                     let path = Path::from_str($path);
-                    let (abs, vol, cwd) = ($abs, $vol, $cwd);
+                    let (abs, vol, cwd, rel) = ($abs, $vol, $cwd, $rel);
                     let b = path.is_absolute();
                     assert!(b == abs, "Path '%s'.is_absolute(): expected %?, found %?",
                             path.as_str().unwrap(), abs, b);
@@ -2089,25 +2292,28 @@ mod tests {
                     let b = path.is_cwd_relative();
                     assert!(b == cwd, "Path '%s'.is_cwd_relative(): expected %?, found %?",
                             path.as_str().unwrap(), cwd, b);
+                    let b = path.is_relative();
+                    assert!(b == rel, "Path '%s'.is_relativf(): expected %?, found %?",
+                            path.as_str().unwrap(), rel, b);
                 }
             )
         )
-        t!("a\\b\\c", false, false, false);
-        t!("\\a\\b\\c", false, true, false);
-        t!("a", false, false, false);
-        t!("\\a", false, true, false);
-        t!(".", false, false, false);
-        t!("\\", false, true, false);
-        t!("..", false, false, false);
-        t!("..\\..", false, false, false);
-        t!("C:a\\b.txt", false, false, true);
-        t!("C:\\a\\b.txt", true, false, false);
-        t!("\\\\server\\share\\a\\b.txt", true, false, false);
-        t!("\\\\?\\a\\b\\c.txt", true, false, false);
-        t!("\\\\?\\C:\\a\\b.txt", true, false, false);
-        t!("\\\\?\\C:a\\b.txt", true, false, false); // NB: not equivalent to C:a\b.txt
-        t!("\\\\?\\UNC\\server\\share\\a\\b.txt", true, false, false);
-        t!("\\\\.\\a\\b", true, false, false);
+        t!("a\\b\\c", false, false, false, true);
+        t!("\\a\\b\\c", false, true, false, false);
+        t!("a", false, false, false, true);
+        t!("\\a", false, true, false, false);
+        t!(".", false, false, false, true);
+        t!("\\", false, true, false, false);
+        t!("..", false, false, false, true);
+        t!("..\\..", false, false, false, true);
+        t!("C:a\\b.txt", false, false, true, false);
+        t!("C:\\a\\b.txt", true, false, false, false);
+        t!("\\\\server\\share\\a\\b.txt", true, false, false, false);
+        t!("\\\\?\\a\\b\\c.txt", true, false, false, false);
+        t!("\\\\?\\C:\\a\\b.txt", true, false, false, false);
+        t!("\\\\?\\C:a\\b.txt", true, false, false, false); // NB: not equivalent to C:a\b.txt
+        t!("\\\\?\\UNC\\server\\share\\a\\b.txt", true, false, false, false);
+        t!("\\\\.\\a\\b", true, false, false, false);
     }
 
     #[test]
@@ -2214,6 +2420,38 @@ mod tests {
     }
 
     #[test]
+    fn test_ends_with_path() {
+        macro_rules! t(
+            (s: $path:expr, $child:expr, $exp:expr) => (
+                {
+                    let path = Path::from_str($path);
+                    let child = Path::from_str($child);
+                    assert_eq!(path.ends_with_path(&child), $exp);
+                }
+            );
+        )
+
+        t!(s: "a\\b\\c", "c", true);
+        t!(s: "a\\b\\c", "d", false);
+        t!(s: "foo\\bar\\quux", "bar", false);
+        t!(s: "foo\\bar\\quux", "barquux", false);
+        t!(s: "a\\b\\c", "b\\c", true);
+        t!(s: "a\\b\\c", "a\\b\\c", true);
+        t!(s: "a\\b\\c", "foo\\a\\b\\c", false);
+        t!(s: "\\a\\b\\c", "a\\b\\c", true);
+        t!(s: "\\a\\b\\c", "\\a\\b\\c", false); // child must be relative
+        t!(s: "\\a\\b\\c", "foo\\a\\b\\c", false);
+        t!(s: "a\\b\\c", "", false);
+        t!(s: "", "", true);
+        t!(s: "\\a\\b\\c", "d\\e\\f", false);
+        t!(s: "a\\b\\c", "a\\b", false);
+        t!(s: "a\\b\\c", "b", false);
+        t!(s: "C:\\a\\b", "b", true);
+        t!(s: "C:\\a\\b", "C:b", false);
+        t!(s: "C:\\a\\b", "C:a\\b", false);
+    }
+
+    #[test]
     fn test_path_relative_from() {
         macro_rules! t(
             (s: $path:expr, $other:expr, $exp:expr) => (
@@ -2348,22 +2586,34 @@ mod tests {
     }
 
     #[test]
-    fn test_component_iter() {
+    fn test_str_component_iter() {
         macro_rules! t(
             (s: $path:expr, $exp:expr) => (
                 {
                     let path = Path::from_str($path);
-                    let comps = path.component_iter().to_owned_vec();
+                    let comps = path.str_component_iter().map(|x|x.unwrap()).to_owned_vec();
                     let exp: &[&str] = $exp;
-                    assert_eq!(comps.as_slice(), exp);
+                    assert!(comps.as_slice() == exp, "str_component_iter: Expected %?, found %?",
+                            comps.as_slice(), exp);
+                    let comps = path.rev_str_component_iter().map(|x|x.unwrap()).to_owned_vec();
+                    let exp = exp.rev_iter().map(|&x|x).to_owned_vec();
+                    assert!(comps.as_slice() == exp,
+                            "rev_str_component_iter: Expected %?, found %?",
+                            comps.as_slice(), exp);
                 }
             );
             (v: [$($arg:expr),+], $exp:expr) => (
                 {
                     let path = Path::from_vec(b!($($arg),+));
-                    let comps = path.component_iter().to_owned_vec();
+                    let comps = path.str_component_iter().map(|x|x.unwrap()).to_owned_vec();
                     let exp: &[&str] = $exp;
-                    assert_eq!(comps.as_slice(), exp);
+                    assert!(comps.as_slice() == exp, "str_component_iter: Expected %?, found %?",
+                            comps.as_slice(), exp);
+                    let comps = path.rev_str_component_iter().map(|x|x.unwrap()).to_owned_vec();
+                    let exp = exp.rev_iter().map(|&x|x).to_owned_vec();
+                    assert!(comps.as_slice() == exp,
+                            "rev_str_component_iter: Expected %?, found %?",
+                            comps.as_slice(), exp);
                 }
             )
         )
@@ -2409,6 +2659,29 @@ mod tests {
     }
 
     #[test]
+    fn test_component_iter() {
+        macro_rules! t(
+            (s: $path:expr, $exp:expr) => (
+                {
+                    let path = Path::from_str($path);
+                    let comps = path.component_iter().to_owned_vec();
+                    let exp: &[&[u8]] = $exp;
+                    assert!(comps.as_slice() == exp, "component_iter: Expected %?, found %?",
+                            comps.as_slice(), exp);
+                    let comps = path.rev_component_iter().to_owned_vec();
+                    let exp = exp.rev_iter().map(|&x|x).to_owned_vec();
+                    assert!(comps.as_slice() == exp, "rev_component_iter: Expected %?, found %?",
+                            comps.as_slice(), exp);
+                }
+            )
+        )
+
+        t!(s: "a\\b\\c", [b!("a"), b!("b"), b!("c")]);
+        t!(s: ".", [b!(".")]);
+        // since this is really a wrapper around str_component_iter, those tests suffice
+    }
+
+    #[test]
     fn test_each_parent() {
         assert!(Path::from_str("/foo/bar").each_parent(|_| true));
         assert!(!Path::from_str("/foo/bar").each_parent(|_| false));
diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs
index 3da337add94..24327e57f82 100644
--- a/src/libstd/prelude.rs
+++ b/src/libstd/prelude.rs
@@ -60,10 +60,7 @@ pub use num::{Algebraic, Trigonometric, Exponential, Hyperbolic};
 pub use num::{Integer, Fractional, Real, RealExt};
 pub use num::{Bitwise, BitCount, Bounded};
 pub use num::{Primitive, Int, Float, ToStrRadix, ToPrimitive, FromPrimitive};
-pub use path::GenericPath;
-pub use path::Path;
-pub use path::PosixPath;
-pub use path::WindowsPath;
+pub use path::{GenericPath, Path, PosixPath, WindowsPath};
 pub use ptr::RawPtr;
 pub use ascii::{Ascii, AsciiCast, OwnedAsciiCast, AsciiStr, ToBytesConsume};
 pub use send_str::{SendStr, SendStrOwned, SendStrStatic, IntoSendStr};
diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs
index 3258c350cd0..9fbb897c2a7 100644
--- a/src/libstd/rt/io/file.rs
+++ b/src/libstd/rt/io/file.rs
@@ -627,12 +627,13 @@ pub trait DirectoryInfo : FileSystemInfo {
     fn mkdir(&self) {
         match ignore_io_error(|| self.stat()) {
             Some(_) => {
+                let path = self.get_path();
                 io_error::cond.raise(IoError {
                     kind: PathAlreadyExists,
                     desc: "Path already exists",
                     detail:
                         Some(format!("{} already exists; can't mkdir it",
-                                     self.get_path().to_str()))
+                                     path.display()))
                 })
             },
             None => mkdir(self.get_path())
@@ -655,24 +656,27 @@ pub trait DirectoryInfo : FileSystemInfo {
                 match s.is_dir {
                     true => rmdir(self.get_path()),
                     false => {
+                        let path = self.get_path();
                         let ioerr = IoError {
                             kind: MismatchedFileTypeForOperation,
                             desc: "Cannot do rmdir() on a non-directory",
                             detail: Some(format!(
                                 "{} is a non-directory; can't rmdir it",
-                                self.get_path().to_str()))
+                                path.display()))
                         };
                         io_error::cond.raise(ioerr);
                     }
                 }
             },
-            None =>
+            None => {
+                let path = self.get_path();
                 io_error::cond.raise(IoError {
                     kind: PathDoesntExist,
                     desc: "Path doesn't exist",
                     detail: Some(format!("{} doesn't exist; can't rmdir it",
-                                         self.get_path().to_str()))
+                                         path.display()))
                 })
+            }
         }
     }
 
@@ -699,7 +703,7 @@ mod test {
     fn file_test_io_smoke_test() {
         do run_in_mt_newsched_task {
             let message = "it's alright. have a good time";
-            let filename = &Path("./tmp/file_rt_io_file_test.txt");
+            let filename = &Path::from_str("./tmp/file_rt_io_file_test.txt");
             {
                 let mut write_stream = open(filename, Create, ReadWrite).unwrap();
                 write_stream.write(message.as_bytes());
@@ -721,7 +725,7 @@ mod test {
     #[test]
     fn file_test_io_invalid_path_opened_without_create_should_raise_condition() {
         do run_in_mt_newsched_task {
-            let filename = &Path("./tmp/file_that_does_not_exist.txt");
+            let filename = &Path::from_str("./tmp/file_that_does_not_exist.txt");
             let mut called = false;
             do io_error::cond.trap(|_| {
                 called = true;
@@ -736,7 +740,7 @@ mod test {
     #[test]
     fn file_test_iounlinking_invalid_path_should_raise_condition() {
         do run_in_mt_newsched_task {
-            let filename = &Path("./tmp/file_another_file_that_does_not_exist.txt");
+            let filename = &Path::from_str("./tmp/file_another_file_that_does_not_exist.txt");
             let mut called = false;
             do io_error::cond.trap(|_| {
                 called = true;
@@ -753,7 +757,7 @@ mod test {
             use str;
             let message = "ten-four";
             let mut read_mem = [0, .. 8];
-            let filename = &Path("./tmp/file_rt_io_file_test_positional.txt");
+            let filename = &Path::from_str("./tmp/file_rt_io_file_test_positional.txt");
             {
                 let mut rw_stream = open(filename, Create, ReadWrite).unwrap();
                 rw_stream.write(message.as_bytes());
@@ -784,7 +788,7 @@ mod test {
             let set_cursor = 4 as u64;
             let mut tell_pos_pre_read;
             let mut tell_pos_post_read;
-            let filename = &Path("./tmp/file_rt_io_file_test_seeking.txt");
+            let filename = &Path::from_str("./tmp/file_rt_io_file_test_seeking.txt");
             {
                 let mut rw_stream = open(filename, Create, ReadWrite).unwrap();
                 rw_stream.write(message.as_bytes());
@@ -813,7 +817,7 @@ mod test {
             let final_msg =     "foo-the-bar!!";
             let seek_idx = 3;
             let mut read_mem = [0, .. 13];
-            let filename = &Path("./tmp/file_rt_io_file_test_seek_and_write.txt");
+            let filename = &Path::from_str("./tmp/file_rt_io_file_test_seek_and_write.txt");
             {
                 let mut rw_stream = open(filename, Create, ReadWrite).unwrap();
                 rw_stream.write(initial_msg.as_bytes());
@@ -839,7 +843,7 @@ mod test {
             let chunk_two = "asdf";
             let chunk_three = "zxcv";
             let mut read_mem = [0, .. 4];
-            let filename = &Path("./tmp/file_rt_io_file_test_seek_shakedown.txt");
+            let filename = &Path::from_str("./tmp/file_rt_io_file_test_seek_shakedown.txt");
             {
                 let mut rw_stream = open(filename, Create, ReadWrite).unwrap();
                 rw_stream.write(initial_msg.as_bytes());
@@ -869,7 +873,7 @@ mod test {
     #[test]
     fn file_test_stat_is_correct_on_is_file() {
         do run_in_mt_newsched_task {
-            let filename = &Path("./tmp/file_stat_correct_on_is_file.txt");
+            let filename = &Path::from_str("./tmp/file_stat_correct_on_is_file.txt");
             {
                 let mut fs = open(filename, Create, ReadWrite).unwrap();
                 let msg = "hw";
@@ -887,7 +891,7 @@ mod test {
     #[test]
     fn file_test_stat_is_correct_on_is_dir() {
         do run_in_mt_newsched_task {
-            let filename = &Path("./tmp/file_stat_correct_on_is_dir");
+            let filename = &Path::from_str("./tmp/file_stat_correct_on_is_dir");
             mkdir(filename);
             let stat_res = match stat(filename) {
                 Some(s) => s,
@@ -901,7 +905,7 @@ mod test {
     #[test]
     fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() {
         do run_in_mt_newsched_task {
-            let dir = &Path("./tmp/fileinfo_false_on_dir");
+            let dir = &Path::from_str("./tmp/fileinfo_false_on_dir");
             mkdir(dir);
             assert!(dir.is_file() == false);
             rmdir(dir);
@@ -911,7 +915,7 @@ mod test {
     #[test]
     fn file_test_fileinfo_check_exists_before_and_after_file_creation() {
         do run_in_mt_newsched_task {
-            let file = &Path("./tmp/fileinfo_check_exists_b_and_a.txt");
+            let file = &Path::from_str("./tmp/fileinfo_check_exists_b_and_a.txt");
             {
                 let msg = "foo".as_bytes();
                 let mut w = file.open_writer(Create);
@@ -926,7 +930,7 @@ mod test {
     #[test]
     fn file_test_directoryinfo_check_exists_before_and_after_mkdir() {
         do run_in_mt_newsched_task {
-            let dir = &Path("./tmp/before_and_after_dir");
+            let dir = &Path::from_str("./tmp/before_and_after_dir");
             assert!(!dir.exists());
             dir.mkdir();
             assert!(dir.exists());
@@ -940,11 +944,11 @@ mod test {
     fn file_test_directoryinfo_readdir() {
         use str;
         do run_in_mt_newsched_task {
-            let dir = &Path("./tmp/di_readdir");
+            let dir = &Path::from_str("./tmp/di_readdir");
             dir.mkdir();
             let prefix = "foo";
             for n in range(0,3) {
-                let f = dir.push(format!("{}.txt", n));
+                let f = dir.join_str(format!("{}.txt", n));
                 let mut w = f.open_writer(Create);
                 let msg_str = (prefix + n.to_str().to_owned()).to_owned();
                 let msg = msg_str.as_bytes();
@@ -955,13 +959,13 @@ mod test {
                     let mut mem = [0u8, .. 4];
                     for f in files.iter() {
                         {
-                            let n = f.filestem();
+                            let n = f.filestem_str();
                             let mut r = f.open_reader(Open);
                             r.read(mem);
                             let read_str = str::from_utf8(mem);
                             let expected = match n {
-                                Some(n) => prefix+n,
-                                None => fail2!("really shouldn't happen..")
+                                None|Some("") => fail2!("really shouldn't happen.."),
+                                Some(n) => prefix+n
                             };
                             assert!(expected == read_str);
                         }
diff --git a/src/libstd/rt/io/support.rs b/src/libstd/rt/io/support.rs
index 59db8194963..a872423c255 100644
--- a/src/libstd/rt/io/support.rs
+++ b/src/libstd/rt/io/support.rs
@@ -22,7 +22,7 @@ impl<'self> PathLike for &'self str {
 
 impl PathLike for Path {
     fn path_as_str<T>(&self, f: &fn(&str) -> T) -> T {
-        let s = self.to_str();
+        let s = self.as_str().unwrap();
         f(s)
     }
 }
@@ -35,7 +35,7 @@ mod test {
     #[test]
     fn path_like_smoke_test() {
         let expected = if cfg!(unix) { "/home" } else { "C:\\" };
-        let path = Path(expected);
+        let path = Path::from_str(expected);
         path.path_as_str(|p| assert!(p == expected));
         path.path_as_str(|p| assert!(p == expected));
     }
diff --git a/src/libstd/rt/test.rs b/src/libstd/rt/test.rs
index b6611eee9e6..1178bfdaa80 100644
--- a/src/libstd/rt/test.rs
+++ b/src/libstd/rt/test.rs
@@ -16,6 +16,7 @@ use container::Container;
 use iter::{Iterator, range};
 use super::io::net::ip::{SocketAddr, Ipv4Addr, Ipv6Addr};
 use vec::{OwnedVector, MutableVector, ImmutableVector};
+use path::GenericPath;
 use rt::sched::Scheduler;
 use unstable::{run_in_bare_thread};
 use rt::thread::Thread;
@@ -346,7 +347,6 @@ it is running in and assigns a port range based on it.
 fn base_port() -> uint {
     use os;
     use str::StrSlice;
-    use to_str::ToStr;
     use vec::ImmutableVector;
 
     let base = 9600u;
@@ -363,12 +363,14 @@ fn base_port() -> uint {
         ("dist", base + range * 8)
     ];
 
-    let path = os::getcwd().to_str();
+    // FIXME (#9639): This needs to handle non-utf8 paths
+    let path = os::getcwd();
+    let path_s = path.as_str().unwrap();
 
     let mut final_base = base;
 
     for &(dir, base) in bases.iter() {
-        if path.contains(dir) {
+        if path_s.contains(dir) {
             final_base = base;
             break;
         }
diff --git a/src/libstd/rt/uv/file.rs b/src/libstd/rt/uv/file.rs
index dc5b512e56e..7756448adf8 100644
--- a/src/libstd/rt/uv/file.rs
+++ b/src/libstd/rt/uv/file.rs
@@ -391,7 +391,7 @@ mod test {
             let read_mem = vec::from_elem(read_buf_len, 0u8);
             let read_buf = slice_to_uv_buf(read_mem);
             let read_buf_ptr: *Buf = &read_buf;
-            let p = Path(path_str);
+            let p = Path::from_str(path_str);
             let open_req = FsRequest::new();
             do open_req.open(&loop_, &p, create_flags as int, mode as int)
             |req, uverr| {
@@ -405,7 +405,7 @@ mod test {
                         assert!(uverr.is_none());
                         let loop_ = req.get_loop();
                         let open_req = FsRequest::new();
-                        do open_req.open(&loop_, &Path(path_str), read_flags as int,0)
+                        do open_req.open(&loop_, &Path::from_str(path_str), read_flags as int,0)
                             |req, uverr| {
                             assert!(uverr.is_none());
                             let loop_ = req.get_loop();
@@ -431,7 +431,7 @@ mod test {
                                         assert!(uverr.is_none());
                                         let loop_ = &req.get_loop();
                                         let unlink_req = FsRequest::new();
-                                        do unlink_req.unlink(loop_, &Path(path_str))
+                                        do unlink_req.unlink(loop_, &Path::from_str(path_str))
                                         |_,uverr| {
                                             assert!(uverr.is_none());
                                         };
@@ -465,7 +465,7 @@ mod test {
             let write_buf = slice_to_uv_buf(write_val);
             // open/create
             let open_req = FsRequest::new();
-            let result = open_req.open_sync(&loop_, &Path(path_str),
+            let result = open_req.open_sync(&loop_, &Path::from_str(path_str),
                                                    create_flags as int, mode as int);
             assert!(result.is_ok());
             let fd = result.unwrap();
@@ -479,7 +479,7 @@ mod test {
             assert!(result.is_ok());
             // re-open
             let open_req = FsRequest::new();
-            let result = open_req.open_sync(&loop_, &Path(path_str),
+            let result = open_req.open_sync(&loop_, &Path::from_str(path_str),
                                                    read_flags as int,0);
             assert!(result.is_ok());
             let len = 1028;
@@ -503,7 +503,7 @@ mod test {
                 assert!(result.is_ok());
                 // unlink
                 let unlink_req = FsRequest::new();
-                let result = unlink_req.unlink_sync(&loop_, &Path(path_str));
+                let result = unlink_req.unlink_sync(&loop_, &Path::from_str(path_str));
                 assert!(result.is_ok());
             } else { fail2!("nread was 0.. wudn't expectin' that."); }
             loop_.close();
diff --git a/src/libstd/rt/uv/uvio.rs b/src/libstd/rt/uv/uvio.rs
index 1de6042003c..a139c4e95ef 100644
--- a/src/libstd/rt/uv/uvio.rs
+++ b/src/libstd/rt/uv/uvio.rs
@@ -34,7 +34,7 @@ use rt::uv::idle::IdleWatcher;
 use rt::uv::net::{UvIpv4SocketAddr, UvIpv6SocketAddr, accum_sockaddrs};
 use rt::uv::addrinfo::GetAddrInfoRequest;
 use unstable::sync::Exclusive;
-use path::Path;
+use path::{GenericPath, Path};
 use super::super::io::support::PathLike;
 use libc::{lseek, off_t, O_CREAT, O_APPEND, O_TRUNC, O_RDWR, O_RDONLY, O_WRONLY,
           S_IRUSR, S_IWUSR, S_IRWXU};
@@ -631,7 +631,7 @@ impl IoFactory for UvIoFactory {
                         None => {
                             let stat = req.get_stat();
                             Ok(FileStat {
-                                path: Path(path_str),
+                                path: Path::from_str(path_str),
                                 is_file: stat.is_file(),
                                 is_dir: stat.is_dir(),
                                 size: stat.st_size,
@@ -720,7 +720,9 @@ impl IoFactory for UvIoFactory {
                             let rel_paths = req.get_paths();
                             let mut paths = ~[];
                             for r in rel_paths.iter() {
-                                paths.push(Path(path_str+"/"+*r));
+                                let mut p = Path::from_str(path_str);
+                                p.push_str(*r);
+                                paths.push(p);
                             }
                             Ok(paths)
                         },
@@ -2177,20 +2179,20 @@ fn file_test_uvio_full_simple_impl() {
         {
             let create_fm = Create;
             let create_fa = ReadWrite;
-            let mut fd = (*io).fs_open(&Path(path), create_fm, create_fa).unwrap();
+            let mut fd = (*io).fs_open(&Path::from_str(path), create_fm, create_fa).unwrap();
             let write_buf = write_val.as_bytes();
             fd.write(write_buf);
         }
         {
             let ro_fm = Open;
             let ro_fa = Read;
-            let mut fd = (*io).fs_open(&Path(path), ro_fm, ro_fa).unwrap();
+            let mut fd = (*io).fs_open(&Path::from_str(path), ro_fm, ro_fa).unwrap();
             let mut read_vec = [0, .. 1028];
             let nread = fd.read(read_vec).unwrap();
             let read_val = str::from_utf8(read_vec.slice(0, nread as uint));
             assert!(read_val == write_val.to_owned());
         }
-        (*io).fs_unlink(&Path(path));
+        (*io).fs_unlink(&Path::from_str(path));
     }
 }
 
diff --git a/src/libstd/run.rs b/src/libstd/run.rs
index 8712d01aae9..3f7ce3eae58 100644
--- a/src/libstd/run.rs
+++ b/src/libstd/run.rs
@@ -578,8 +578,8 @@ mod tests {
         let mut prog = run_pwd(None);
 
         let output = str::from_utf8(prog.finish_with_output().output);
-        let parent_dir = os::getcwd().normalize();
-        let child_dir = Path(output.trim()).normalize();
+        let parent_dir = os::getcwd();
+        let child_dir = Path::from_str(output.trim());
 
         let parent_stat = parent_dir.stat().unwrap();
         let child_stat = child_dir.stat().unwrap();
@@ -592,11 +592,11 @@ mod tests {
     fn test_change_working_directory() {
         // test changing to the parent of os::getcwd() because we know
         // the path exists (and os::getcwd() is not expected to be root)
-        let parent_dir = os::getcwd().dir_path().normalize();
+        let parent_dir = os::getcwd().dir_path();
         let mut prog = run_pwd(Some(&parent_dir));
 
         let output = str::from_utf8(prog.finish_with_output().output);
-        let child_dir = Path(output.trim()).normalize();
+        let child_dir = Path::from_str(output.trim());
 
         let parent_stat = parent_dir.stat().unwrap();
         let child_stat = child_dir.stat().unwrap();
diff --git a/src/libstd/std.rs b/src/libstd/std.rs
index e8109124e57..73cb6a5645d 100644
--- a/src/libstd/std.rs
+++ b/src/libstd/std.rs
@@ -177,7 +177,6 @@ pub mod libc;
 pub mod c_str;
 pub mod os;
 pub mod path;
-pub mod path2;
 pub mod rand;
 pub mod run;
 pub mod sys;
diff --git a/src/libstd/unstable/dynamic_lib.rs b/src/libstd/unstable/dynamic_lib.rs
index 62ff8c9fbc8..a91d68366f3 100644
--- a/src/libstd/unstable/dynamic_lib.rs
+++ b/src/libstd/unstable/dynamic_lib.rs
@@ -225,7 +225,7 @@ pub mod dl {
 
     pub unsafe fn open_external(filename: &path::Path) -> *libc::c_void {
         #[fixed_stack_segment]; #[inline(never)];
-        do os::win32::as_utf16_p(filename.to_str()) |raw_name| {
+        do os::win32::as_utf16_p(filename.as_str().unwrap()) |raw_name| {
             LoadLibraryW(raw_name)
         }
     }