1 // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
13 Cross-platform file path handling
17 #[allow(missing_doc)];
20 use container::Container;
22 use iterator::IteratorUtil;
24 use option::{None, Option, Some};
25 use str::{OwnedStr, Str, StrSlice, StrVector};
28 use ascii::{AsciiCast, AsciiStr};
29 use vec::{OwnedVector, ImmutableVector};
32 pub use Path = self::WindowsPath;
34 pub use Path = self::PosixPath;
36 #[deriving(Clone, Eq)]
37 pub struct WindowsPath {
44 pub fn WindowsPath(s: &str) -> WindowsPath {
45 GenericPath::from_str(s)
48 #[deriving(Clone, Eq)]
49 pub struct PosixPath {
54 pub fn PosixPath(s: &str) -> PosixPath {
55 GenericPath::from_str(s)
58 pub trait GenericPath {
59 /// Converts a string to a Path
60 fn from_str(&str) -> Self;
62 /// Returns the directory component of `self`, as a string
63 fn dirname(&self) -> ~str;
64 /// Returns the file component of `self`, as a string option.
65 /// Returns None if `self` names a directory.
66 fn filename(&self) -> Option<~str>;
67 /// Returns the stem of the file component of `self`, as a string option.
68 /// The stem is the slice of a filename starting at 0 and ending just before
69 /// the last '.' in the name.
70 /// Returns None if `self` names a directory.
71 fn filestem(&self) -> Option<~str>;
72 /// Returns the type of the file component of `self`, as a string option.
73 /// The file type is the slice of a filename starting just after the last
74 /// '.' in the name and ending at the last index in the filename.
75 /// Returns None if `self` names a directory.
76 fn filetype(&self) -> Option<~str>;
78 /// Returns a new path consisting of `self` with the parent directory component replaced
79 /// with the given string.
80 fn with_dirname(&self, (&str)) -> Self;
81 /// Returns a new path consisting of `self` with the file component replaced
82 /// with the given string.
83 fn with_filename(&self, (&str)) -> Self;
84 /// Returns a new path consisting of `self` with the file stem replaced
85 /// with the given string.
86 fn with_filestem(&self, (&str)) -> Self;
87 /// Returns a new path consisting of `self` with the file type replaced
88 /// with the given string.
89 fn with_filetype(&self, (&str)) -> Self;
91 /// Returns the directory component of `self`, as a new path.
92 /// If `self` has no parent, returns `self`.
93 fn dir_path(&self) -> Self;
94 /// Returns the file component of `self`, as a new path.
95 /// If `self` names a directory, returns the empty path.
96 fn file_path(&self) -> Self;
98 /// Returns a new Path whose parent directory is `self` and whose
99 /// file component is the given string.
100 fn push(&self, (&str)) -> Self;
101 /// Returns a new Path consisting of the given path, made relative to `self`.
102 fn push_rel(&self, (&Self)) -> Self;
103 /// Returns a new Path consisting of the path given by the given vector
104 /// of strings, relative to `self`.
105 fn push_many<S: Str>(&self, (&[S])) -> Self;
106 /// Identical to `dir_path` except in the case where `self` has only one
107 /// component. In this case, `pop` returns the empty path.
108 fn pop(&self) -> Self;
110 /// The same as `push_rel`, except that the directory argument must not
111 /// contain directory separators in any of its components.
112 fn unsafe_join(&self, (&Self)) -> Self;
113 /// On Unix, always returns false. On Windows, returns true iff `self`'s
114 /// file stem is one of: `con` `aux` `com1` `com2` `com3` `com4`
115 /// `lpt1` `lpt2` `lpt3` `prn` `nul`
116 fn is_restricted(&self) -> bool;
118 /// Returns a new path that names the same file as `self`, without containing
119 /// any '.', '..', or empty components. On Windows, uppercases the drive letter
121 fn normalize(&self) -> Self;
123 /// Returns `true` if `self` is an absolute path.
124 fn is_absolute(&self) -> bool;
126 /// True if `self` is an ancestor of `other`. See `test_is_ancestor_of` for examples
127 fn is_ancestor_of(&self, (&Self)) -> bool;
130 #[cfg(target_os = "linux")]
131 #[cfg(target_os = "android")]
133 #[cfg(target_arch = "x86")]
137 pub fn default_stat() -> libc::stat {
163 #[cfg(target_arch = "arm")]
167 pub fn default_stat() -> libc::stat {
192 #[cfg(target_arch = "mips")]
196 pub fn default_stat() -> libc::stat {
222 #[cfg(target_arch = "x86_64")]
226 pub fn default_stat() -> libc::stat {
251 #[cfg(target_os = "freebsd")]
253 #[cfg(target_arch = "x86_64")]
257 pub fn default_stat() -> libc::stat {
279 st_birthtime_nsec: 0,
286 #[cfg(target_os = "macos")]
291 pub fn default_stat() -> libc::stat {
307 st_birthtime_nsec: 0,
320 #[cfg(target_os = "win32")]
324 pub fn default_stat() -> libc::stat {
342 #[cfg(target_os = "win32")]
344 pub fn stat(&self) -> Option<libc::stat> {
346 do str::as_c_str(self.to_str()) |buf| {
347 let mut st = stat::arch::default_stat();
348 match libc::stat(buf, &mut st) {
356 pub fn exists(&self) -> bool {
363 pub fn get_size(&self) -> Option<i64> {
366 Some(ref st) => Some(st.st_size as i64),
370 pub fn get_mode(&self) -> Option<uint> {
373 Some(ref st) => Some(st.st_mode as uint),
378 #[cfg(not(target_os = "win32"))]
380 pub fn stat(&self) -> Option<libc::stat> {
382 do str::as_c_str(self.to_str()) |buf| {
383 let mut st = stat::arch::default_stat();
384 match libc::stat(buf, &mut st) {
392 pub fn exists(&self) -> bool {
399 pub fn get_size(&self) -> Option<i64> {
402 Some(ref st) => Some(st.st_size as i64),
406 pub fn get_mode(&self) -> Option<uint> {
409 Some(ref st) => Some(st.st_mode as uint),
413 /// Execute a function on p as well as all of its ancestors
414 pub fn each_parent(&self, f: &fn(&Path)) {
415 if !self.components.is_empty() {
417 self.pop().each_parent(f);
423 #[cfg(target_os = "freebsd")]
424 #[cfg(target_os = "linux")]
425 #[cfg(target_os = "macos")]
427 pub fn get_atime(&self) -> Option<(i64, int)> {
431 Some((st.st_atime as i64,
432 st.st_atime_nsec as int))
437 pub fn get_mtime(&self) -> Option<(i64, int)> {
441 Some((st.st_mtime as i64,
442 st.st_mtime_nsec as int))
447 pub fn get_ctime(&self) -> Option<(i64, int)> {
451 Some((st.st_ctime as i64,
452 st.st_ctime_nsec as int))
460 pub fn lstat(&self) -> Option<libc::stat> {
462 do str::as_c_str(self.to_str()) |buf| {
463 let mut st = stat::arch::default_stat();
464 match libc::lstat(buf, &mut st) {
473 #[cfg(target_os = "freebsd")]
474 #[cfg(target_os = "macos")]
476 pub fn get_birthtime(&self) -> Option<(i64, int)> {
480 Some((st.st_birthtime as i64,
481 st.st_birthtime_nsec as int))
487 #[cfg(target_os = "win32")]
489 pub fn get_atime(&self) -> Option<(i64, int)> {
493 Some((st.st_atime as i64, 0))
498 pub fn get_mtime(&self) -> Option<(i64, int)> {
502 Some((st.st_mtime as i64, 0))
507 pub fn get_ctime(&self) -> Option<(i64, int)> {
511 Some((st.st_ctime as i64, 0))
516 /// Execute a function on p as well as all of its ancestors
517 pub fn each_parent(&self, f: &fn(&Path)) {
518 if !self.components.is_empty() {
520 self.pop().each_parent(f);
525 impl ToStr for PosixPath {
526 fn to_str(&self) -> ~str {
528 if self.is_absolute {
531 s + self.components.connect("/")
535 // FIXME (#3227): when default methods in traits are working, de-duplicate
536 // PosixPath and WindowsPath, most of their methods are common.
537 impl GenericPath for PosixPath {
538 fn from_str(s: &str) -> PosixPath {
539 let components = s.split_iter('/')
540 .filter_map(|s| if s.is_empty() {None} else {Some(s.to_owned())})
542 let is_absolute = (s.len() != 0 && s[0] == '/' as u8);
544 is_absolute: is_absolute,
545 components: components,
549 fn dirname(&self) -> ~str {
550 let s = self.dir_path().to_str();
557 fn filename(&self) -> Option<~str> {
558 match self.components.len() {
560 n => Some(self.components[n - 1].clone()),
564 fn filestem(&self) -> Option<~str> {
565 match self.filename() {
569 Some(p) => Some(f.slice_to(p).to_owned()),
570 None => Some((*f).clone()),
576 fn filetype(&self) -> Option<~str> {
577 match self.filename() {
581 Some(p) if p < f.len() => Some(f.slice_from(p).to_owned()),
588 fn with_dirname(&self, d: &str) -> PosixPath {
589 let dpath = PosixPath(d);
590 match self.filename() {
591 Some(ref f) => dpath.push(*f),
596 fn with_filename(&self, f: &str) -> PosixPath {
597 assert!(! f.iter().all(windows::is_sep));
598 self.dir_path().push(f)
601 fn with_filestem(&self, s: &str) -> PosixPath {
602 match self.filetype() {
603 None => self.with_filename(s),
604 Some(ref t) => self.with_filename(s.to_owned() + *t),
608 fn with_filetype(&self, t: &str) -> PosixPath {
609 match (t.len(), self.filestem()) {
610 (0, None) => (*self).clone(),
611 (0, Some(ref s)) => self.with_filename(*s),
612 (_, None) => self.with_filename(fmt!(".%s", t)),
613 (_, Some(ref s)) => self.with_filename(fmt!("%s.%s", *s, t)),
617 fn dir_path(&self) -> PosixPath {
618 match self.components.len() {
619 0 => (*self).clone(),
624 fn file_path(&self) -> PosixPath {
625 let cs = match self.filename() {
627 Some(ref f) => ~[(*f).clone()]
635 fn push_rel(&self, other: &PosixPath) -> PosixPath {
636 assert!(!other.is_absolute);
637 self.push_many(other.components)
640 fn unsafe_join(&self, other: &PosixPath) -> PosixPath {
641 if other.is_absolute {
644 components: other.components.clone(),
651 fn is_restricted(&self) -> bool {
655 fn push_many<S: Str>(&self, cs: &[S]) -> PosixPath {
656 let mut v = self.components.clone();
657 for cs.iter().advance |e| {
658 for e.as_slice().split_iter(windows::is_sep).advance |s| {
665 is_absolute: self.is_absolute,
670 fn push(&self, s: &str) -> PosixPath {
671 let mut v = self.components.clone();
672 for s.split_iter(windows::is_sep).advance |s| {
683 fn pop(&self) -> PosixPath {
684 let mut cs = self.components.clone();
689 is_absolute: self.is_absolute,
694 fn normalize(&self) -> PosixPath {
696 is_absolute: self.is_absolute,
697 components: normalize(self.components),
701 fn is_absolute(&self) -> bool {
705 fn is_ancestor_of(&self, other: &PosixPath) -> bool {
706 debug!("%s / %s %? %?", self.to_str(), other.to_str(), self.is_absolute,
707 self.components.len());
709 (!other.components.is_empty() && !(self.components.is_empty() && !self.is_absolute) &&
710 self.is_ancestor_of(&other.pop()))
716 impl ToStr for WindowsPath {
717 fn to_str(&self) -> ~str {
733 if self.is_absolute {
736 s + self.components.connect("\\")
741 impl GenericPath for WindowsPath {
742 fn from_str(s: &str) -> WindowsPath {
748 windows::extract_drive_prefix(s),
749 windows::extract_unc_prefix(s),
751 (Some((ref d, ref r)), _) => {
753 device = Some((*d).clone());
756 (None, Some((ref h, ref r))) => {
757 host = Some((*h).clone());
768 let components = rest.split_iter(windows::is_sep)
769 .filter_map(|s| if s.is_empty() {None} else {Some(s.to_owned())})
772 let is_absolute = (rest.len() != 0 && windows::is_sep(rest[0] as char));
776 is_absolute: is_absolute,
777 components: components,
781 fn dirname(&self) -> ~str {
782 let s = self.dir_path().to_str();
789 fn filename(&self) -> Option<~str> {
790 match self.components.len() {
792 n => Some(self.components[n - 1].clone()),
796 fn filestem(&self) -> Option<~str> {
797 match self.filename() {
801 Some(p) => Some(f.slice_to(p).to_owned()),
802 None => Some((*f).clone()),
808 fn filetype(&self) -> Option<~str> {
809 match self.filename() {
813 Some(p) if p < f.len() => Some(f.slice_from(p).to_owned()),
820 fn with_dirname(&self, d: &str) -> WindowsPath {
821 let dpath = WindowsPath(d);
822 match self.filename() {
823 Some(ref f) => dpath.push(*f),
828 fn with_filename(&self, f: &str) -> WindowsPath {
829 assert!(! f.iter().all(windows::is_sep));
830 self.dir_path().push(f)
833 fn with_filestem(&self, s: &str) -> WindowsPath {
834 match self.filetype() {
835 None => self.with_filename(s),
836 Some(ref t) => self.with_filename(s.to_owned() + *t),
840 fn with_filetype(&self, t: &str) -> WindowsPath {
841 match (t.len(), self.filestem()) {
842 (0, None) => (*self).clone(),
843 (0, Some(ref s)) => self.with_filename(*s),
844 (_, None) => self.with_filename(fmt!(".%s", t)),
845 (_, Some(ref s)) => self.with_filename(fmt!("%s.%s", *s, t)),
849 fn dir_path(&self) -> WindowsPath {
850 match self.components.len() {
851 0 => (*self).clone(),
856 fn file_path(&self) -> WindowsPath {
861 components: match self.filename() {
863 Some(ref f) => ~[(*f).clone()],
868 fn push_rel(&self, other: &WindowsPath) -> WindowsPath {
869 assert!(!other.is_absolute);
870 self.push_many(other.components)
873 fn unsafe_join(&self, other: &WindowsPath) -> WindowsPath {
874 /* rhs not absolute is simple push */
875 if !other.is_absolute {
876 return self.push_many(other.components);
879 /* if rhs has a host set, then the whole thing wins */
883 host: Some((*host).clone()),
884 device: other.device.clone(),
886 components: other.components.clone(),
892 /* if rhs has a device set, then a part wins */
894 Some(ref device) => {
897 device: Some((*device).clone()),
899 components: other.components.clone(),
905 /* fallback: host and device of lhs win, but the
906 whole path of the right */
908 host: self.host.clone(),
909 device: self.device.clone(),
910 is_absolute: self.is_absolute || other.is_absolute,
911 components: other.components.clone(),
915 fn is_restricted(&self) -> bool {
916 match self.filestem() {
918 // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use
919 // to_ascii_consume and to_str_consume to not do a unnecessary copy.
920 match stem.to_ascii().to_lower().to_str_ascii() {
921 ~"con" | ~"aux" | ~"com1" | ~"com2" | ~"com3" | ~"com4" |
922 ~"lpt1" | ~"lpt2" | ~"lpt3" | ~"prn" | ~"nul" => true,
930 fn push_many<S: Str>(&self, cs: &[S]) -> WindowsPath {
931 let mut v = self.components.clone();
932 for cs.iter().advance |e| {
933 for e.as_slice().split_iter(windows::is_sep).advance |s| {
939 // tedious, but as-is, we can't use ..self
941 host: self.host.clone(),
942 device: self.device.clone(),
943 is_absolute: self.is_absolute,
948 fn push(&self, s: &str) -> WindowsPath {
949 let mut v = self.components.clone();
950 for s.split_iter(windows::is_sep).advance |s| {
955 WindowsPath { components: v, ..(*self).clone() }
958 fn pop(&self) -> WindowsPath {
959 let mut cs = self.components.clone();
964 host: self.host.clone(),
965 device: self.device.clone(),
966 is_absolute: self.is_absolute,
971 fn normalize(&self) -> WindowsPath {
973 host: self.host.clone(),
974 device: match self.device {
977 // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use
978 // to_ascii_consume and to_str_consume to not do a unnecessary copy.
979 Some(ref device) => Some(device.to_ascii().to_upper().to_str_ascii())
981 is_absolute: self.is_absolute,
982 components: normalize(self.components)
986 fn is_absolute(&self) -> bool {
990 fn is_ancestor_of(&self, other: &WindowsPath) -> bool {
992 (!other.components.is_empty() && !(self.components.is_empty() && !self.is_absolute) &&
993 self.is_ancestor_of(&other.pop()))
997 pub fn normalize(components: &[~str]) -> ~[~str] {
999 for components.iter().advance |c| {
1000 if *c == ~"." && components.len() > 1 { loop; }
1001 if *c == ~"" { loop; }
1002 if *c == ~".." && cs.len() != 0 {
1006 cs.push((*c).clone());
1011 // Various windows helpers, and tests for the impl.
1014 use option::{None, Option, Some};
1017 pub fn is_sep(u: char) -> bool {
1018 u == '/' || u == '\\'
1021 pub fn extract_unc_prefix(s: &str) -> Option<(~str,~str)> {
1023 (s[0] == '\\' as u8 || s[0] == '/' as u8) &&
1027 if is_sep(s[i] as char) {
1028 let pre = s.slice(2, i).to_owned();
1029 let rest = s.slice(i, s.len()).to_owned();
1030 return Some((pre, rest));
1038 pub fn extract_drive_prefix(s: &str) -> Option<(~str,~str)> {
1041 libc::isalpha(s[0] as libc::c_int) != 0 &&
1042 s[1] == ':' as u8) {
1043 let rest = if s.len() == 2 {
1046 s.slice(2, s.len()).to_owned()
1048 return Some((s.slice(0,1).to_owned(), rest));
1057 use option::{None, Some};
1058 use path::{PosixPath, WindowsPath, windows};
1061 fn test_double_slash_collapsing() {
1062 let path = PosixPath("tmp/");
1063 let path = path.push("/hmm");
1064 let path = path.normalize();
1065 assert_eq!(~"tmp/hmm", path.to_str());
1067 let path = WindowsPath("tmp/");
1068 let path = path.push("/hmm");
1069 let path = path.normalize();
1070 assert_eq!(~"tmp\\hmm", path.to_str());
1074 fn test_filetype_foo_bar() {
1075 let wp = PosixPath("foo.bar");
1076 assert_eq!(wp.filetype(), Some(~".bar"));
1078 let wp = WindowsPath("foo.bar");
1079 assert_eq!(wp.filetype(), Some(~".bar"));
1083 fn test_filetype_foo() {
1084 let wp = PosixPath("foo");
1085 assert_eq!(wp.filetype(), None);
1087 let wp = WindowsPath("foo");
1088 assert_eq!(wp.filetype(), None);
1092 fn test_posix_paths() {
1093 fn t(wp: &PosixPath, s: &str) {
1094 let ss = wp.to_str();
1095 let sss = s.to_owned();
1097 debug!("got %s", ss);
1098 debug!("expected %s", sss);
1099 assert_eq!(ss, sss);
1103 t(&(PosixPath("hi")), "hi");
1104 t(&(PosixPath("/lib")), "/lib");
1105 t(&(PosixPath("hi/there")), "hi/there");
1106 t(&(PosixPath("hi/there.txt")), "hi/there.txt");
1108 t(&(PosixPath("hi/there.txt")), "hi/there.txt");
1109 t(&(PosixPath("hi/there.txt")
1110 .with_filetype("")), "hi/there");
1112 t(&(PosixPath("/a/b/c/there.txt")
1113 .with_dirname("hi")), "hi/there.txt");
1115 t(&(PosixPath("hi/there.txt")
1116 .with_dirname(".")), "./there.txt");
1118 t(&(PosixPath("a/b/c")
1119 .push("..")), "a/b/c/..");
1121 t(&(PosixPath("there.txt")
1122 .with_filetype("o")), "there.o");
1124 t(&(PosixPath("hi/there.txt")
1125 .with_filetype("o")), "hi/there.o");
1127 t(&(PosixPath("hi/there.txt")
1129 .with_dirname("/usr/lib")),
1130 "/usr/lib/there.o");
1132 t(&(PosixPath("hi/there.txt")
1134 .with_dirname("/usr/lib/")),
1135 "/usr/lib/there.o");
1137 t(&(PosixPath("hi/there.txt")
1139 .with_dirname("/usr//lib//")),
1140 "/usr/lib/there.o");
1142 t(&(PosixPath("/usr/bin/rust")
1143 .push_many([~"lib", ~"thingy.so"])
1144 .with_filestem("librustc")),
1145 "/usr/bin/rust/lib/librustc.so");
1150 fn test_normalize() {
1151 fn t(wp: &PosixPath, s: &str) {
1152 let ss = wp.to_str();
1153 let sss = s.to_owned();
1155 debug!("got %s", ss);
1156 debug!("expected %s", sss);
1157 assert_eq!(ss, sss);
1161 t(&(PosixPath("hi/there.txt")
1162 .with_dirname(".").normalize()), "there.txt");
1164 t(&(PosixPath("a/b/../c/././/../foo.txt/").normalize()),
1167 t(&(PosixPath("a/b/c")
1168 .push("..").normalize()), "a/b");
1172 fn test_extract_unc_prefixes() {
1173 assert!(windows::extract_unc_prefix("\\\\").is_none());
1174 assert!(windows::extract_unc_prefix("//").is_none());
1175 assert!(windows::extract_unc_prefix("\\\\hi").is_none());
1176 assert!(windows::extract_unc_prefix("//hi").is_none());
1177 assert!(windows::extract_unc_prefix("\\\\hi\\") ==
1178 Some((~"hi", ~"\\")));
1179 assert!(windows::extract_unc_prefix("//hi\\") ==
1180 Some((~"hi", ~"\\")));
1181 assert!(windows::extract_unc_prefix("\\\\hi\\there") ==
1182 Some((~"hi", ~"\\there")));
1183 assert!(windows::extract_unc_prefix("//hi/there") ==
1184 Some((~"hi", ~"/there")));
1185 assert!(windows::extract_unc_prefix(
1186 "\\\\hi\\there\\friends.txt") ==
1187 Some((~"hi", ~"\\there\\friends.txt")));
1188 assert!(windows::extract_unc_prefix(
1189 "//hi\\there\\friends.txt") ==
1190 Some((~"hi", ~"\\there\\friends.txt")));
1194 fn test_extract_drive_prefixes() {
1195 assert!(windows::extract_drive_prefix("c").is_none());
1196 assert!(windows::extract_drive_prefix("c:") ==
1198 assert!(windows::extract_drive_prefix("d:") ==
1200 assert!(windows::extract_drive_prefix("z:") ==
1202 assert!(windows::extract_drive_prefix("c:\\hi") ==
1203 Some((~"c", ~"\\hi")));
1204 assert!(windows::extract_drive_prefix("d:hi") ==
1205 Some((~"d", ~"hi")));
1206 assert!(windows::extract_drive_prefix("c:hi\\there.txt") ==
1207 Some((~"c", ~"hi\\there.txt")));
1208 assert!(windows::extract_drive_prefix("c:\\hi\\there.txt") ==
1209 Some((~"c", ~"\\hi\\there.txt")));
1213 fn test_windows_paths() {
1214 fn t(wp: &WindowsPath, s: &str) {
1215 let ss = wp.to_str();
1216 let sss = s.to_owned();
1218 debug!("got %s", ss);
1219 debug!("expected %s", sss);
1220 assert_eq!(ss, sss);
1224 t(&(WindowsPath("hi")), "hi");
1225 t(&(WindowsPath("hi/there")), "hi\\there");
1226 t(&(WindowsPath("hi/there.txt")), "hi\\there.txt");
1228 t(&(WindowsPath("there.txt")
1229 .with_filetype("o")), "there.o");
1231 t(&(WindowsPath("hi/there.txt")
1232 .with_filetype("o")), "hi\\there.o");
1234 t(&(WindowsPath("hi/there.txt")
1236 .with_dirname("c:\\program files A")),
1237 "c:\\program files A\\there.o");
1239 t(&(WindowsPath("hi/there.txt")
1241 .with_dirname("c:\\program files B\\")),
1242 "c:\\program files B\\there.o");
1244 t(&(WindowsPath("hi/there.txt")
1246 .with_dirname("c:\\program files C\\/")),
1247 "c:\\program files C\\there.o");
1249 t(&(WindowsPath("c:\\program files (x86)\\rust")
1250 .push_many([~"lib", ~"thingy.dll"])
1251 .with_filename("librustc.dll")),
1252 "c:\\program files (x86)\\rust\\lib\\librustc.dll");
1254 t(&(WindowsPath("\\\\computer\\share")
1255 .unsafe_join(&WindowsPath("\\a"))),
1258 t(&(WindowsPath("//computer/share")
1259 .unsafe_join(&WindowsPath("\\a"))),
1262 t(&(WindowsPath("//computer/share")
1263 .unsafe_join(&WindowsPath("\\\\computer\\share"))),
1264 "\\\\computer\\share");
1266 t(&(WindowsPath("C:/whatever")
1267 .unsafe_join(&WindowsPath("//computer/share/a/b"))),
1268 "\\\\computer\\share\\a\\b");
1270 t(&(WindowsPath("C:")
1271 .unsafe_join(&WindowsPath("D:/foo"))),
1274 t(&(WindowsPath("C:")
1275 .unsafe_join(&WindowsPath("B"))),
1278 t(&(WindowsPath("C:")
1279 .unsafe_join(&WindowsPath("/foo"))),
1282 t(&(WindowsPath("C:\\")
1283 .unsafe_join(&WindowsPath("\\bar"))),
1287 .unsafe_join(&WindowsPath(""))),
1291 .unsafe_join(&WindowsPath("a"))),
1295 .unsafe_join(&WindowsPath("C:\\a"))),
1298 t(&(WindowsPath("c:\\foo")
1304 fn test_windows_path_restrictions() {
1305 assert_eq!(WindowsPath("hi").is_restricted(), false);
1306 assert_eq!(WindowsPath("C:\\NUL").is_restricted(), true);
1307 assert_eq!(WindowsPath("C:\\COM1.TXT").is_restricted(), true);
1308 assert_eq!(WindowsPath("c:\\prn.exe").is_restricted(), true);
1312 fn test_is_ancestor_of() {
1313 assert!(&PosixPath("/a/b").is_ancestor_of(&PosixPath("/a/b/c/d")));
1314 assert!(!&PosixPath("/a/b/c/d").is_ancestor_of(&PosixPath("/a/b")));
1315 assert!(!&PosixPath("/a/b").is_ancestor_of(&PosixPath("/c/d")));
1316 assert!(&PosixPath("/a/b").is_ancestor_of(&PosixPath("/a/b/c/d")));
1317 assert!(&PosixPath("/").is_ancestor_of(&PosixPath("/a/b/c")));
1318 assert!(!&PosixPath("/").is_ancestor_of(&PosixPath("")));
1319 assert!(!&PosixPath("/a/b/c").is_ancestor_of(&PosixPath("")));
1320 assert!(!&PosixPath("").is_ancestor_of(&PosixPath("/a/b/c")));
1322 assert!(&WindowsPath("C:\\a\\b").is_ancestor_of(&WindowsPath("C:\\a\\b\\c\\d")));
1323 assert!(!&WindowsPath("C:\\a\\b\\c\\d").is_ancestor_of(&WindowsPath("C:\\a\\b")));
1324 assert!(!&WindowsPath("C:\\a\\b").is_ancestor_of(&WindowsPath("C:\\c\\d")));
1325 assert!(&WindowsPath("C:\\a\\b").is_ancestor_of(&WindowsPath("C:\\a\\b\\c\\d")));
1326 assert!(&WindowsPath("C:\\").is_ancestor_of(&WindowsPath("C:\\a\\b\\c")));
1327 assert!(!&WindowsPath("C:\\").is_ancestor_of(&WindowsPath("")));
1328 assert!(!&WindowsPath("C:\\a\\b\\c").is_ancestor_of(&WindowsPath("")));
1329 assert!(!&WindowsPath("").is_ancestor_of(&WindowsPath("C:\\a\\b\\c")));