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)];
23 use container::Container;
24 use iterator::{Iterator, range};
27 use option::{None, Option, Some};
28 use str::{OwnedStr, Str, StrSlice, StrVector};
30 use ascii::{AsciiCast, AsciiStr};
31 use vec::{OwnedVector, ImmutableVector, OwnedCopyableVector};
34 pub use Path = self::WindowsPath;
36 pub use Path = self::PosixPath;
38 #[deriving(Clone, Eq)]
39 pub struct WindowsPath {
46 pub fn WindowsPath(s: &str) -> WindowsPath {
47 GenericPath::from_str(s)
50 #[deriving(Clone, Eq)]
51 pub struct PosixPath {
56 pub fn PosixPath(s: &str) -> PosixPath {
57 GenericPath::from_str(s)
60 pub trait GenericPath {
61 /// Converts a string to a Path
62 fn from_str(&str) -> Self;
64 /// Returns the directory component of `self`, as a string
65 fn dirname(&self) -> ~str;
66 /// Returns the file component of `self`, as a string option.
67 /// Returns None if `self` names a directory.
68 fn filename(&self) -> Option<~str>;
69 /// Returns the stem of the file component of `self`, as a string option.
70 /// The stem is the slice of a filename starting at 0 and ending just before
71 /// the last '.' in the name.
72 /// Returns None if `self` names a directory.
73 fn filestem(&self) -> Option<~str>;
74 /// Returns the type of the file component of `self`, as a string option.
75 /// The file type is the slice of a filename starting just after the last
76 /// '.' in the name and ending at the last index in the filename.
77 /// Returns None if `self` names a directory.
78 fn filetype(&self) -> Option<~str>;
80 /// Returns a new path consisting of `self` with the parent directory component replaced
81 /// with the given string.
82 fn with_dirname(&self, (&str)) -> Self;
83 /// Returns a new path consisting of `self` with the file component replaced
84 /// with the given string.
85 fn with_filename(&self, (&str)) -> Self;
86 /// Returns a new path consisting of `self` with the file stem replaced
87 /// with the given string.
88 fn with_filestem(&self, (&str)) -> Self;
89 /// Returns a new path consisting of `self` with the file type replaced
90 /// with the given string.
91 fn with_filetype(&self, (&str)) -> Self;
93 /// Returns the directory component of `self`, as a new path.
94 /// If `self` has no parent, returns `self`.
95 fn dir_path(&self) -> Self;
96 /// Returns the file component of `self`, as a new path.
97 /// If `self` names a directory, returns the empty path.
98 fn file_path(&self) -> Self;
100 /// Returns a new Path whose parent directory is `self` and whose
101 /// file component is the given string.
102 fn push(&self, (&str)) -> Self;
103 /// Returns a new Path consisting of the given path, made relative to `self`.
104 fn push_rel(&self, (&Self)) -> Self;
105 /// Returns a new Path consisting of the path given by the given vector
106 /// of strings, relative to `self`.
107 fn push_many<S: Str>(&self, (&[S])) -> Self;
108 /// Identical to `dir_path` except in the case where `self` has only one
109 /// component. In this case, `pop` returns the empty path.
110 fn pop(&self) -> Self;
112 /// The same as `push_rel`, except that the directory argument must not
113 /// contain directory separators in any of its components.
114 fn unsafe_join(&self, (&Self)) -> Self;
115 /// On Unix, always returns false. On Windows, returns true iff `self`'s
116 /// file stem is one of: `con` `aux` `com1` `com2` `com3` `com4`
117 /// `lpt1` `lpt2` `lpt3` `prn` `nul`
118 fn is_restricted(&self) -> bool;
120 /// Returns a new path that names the same file as `self`, without containing
121 /// any '.', '..', or empty components. On Windows, uppercases the drive letter
123 fn normalize(&self) -> Self;
125 /// Returns `true` if `self` is an absolute path.
126 fn is_absolute(&self) -> bool;
128 /// True if `self` is an ancestor of `other`. See `test_is_ancestor_of` for examples
129 fn is_ancestor_of(&self, (&Self)) -> bool;
131 /// Find the relative path from one file to another
132 fn get_relative_to(&self, abs2: (&Self)) -> Self {
133 assert!(self.is_absolute());
134 assert!(abs2.is_absolute());
135 let abs1 = self.normalize();
136 let abs2 = abs2.normalize();
138 let split1: &[~str] = abs1.components();
139 let split2: &[~str] = abs2.components();
140 let len1 = split1.len();
141 let len2 = split2.len();
145 let max_common_path = num::min(len1, len2) - 1;
146 let mut start_idx = 0;
147 while start_idx < max_common_path
148 && split1[start_idx] == split2[start_idx] {
152 let mut path: ~[~str] = ~[];
153 for _ in range(start_idx, len1 - 1) { path.push(~".."); };
155 path.push_all(split2.slice(start_idx, len2 - 1));
157 let mut result: Self = GenericPath::from_str(".");
158 if !path.is_empty() {
159 // Without this type hint, the typechecker doesn't seem to like it
160 let p: Self = GenericPath::from_str("");
161 result = p.push_many(path);
166 fn components(self) -> ~[~str];
169 #[cfg(target_os = "linux")]
170 #[cfg(target_os = "android")]
172 #[cfg(target_arch = "x86")]
176 pub fn default_stat() -> libc::stat {
202 #[cfg(target_arch = "arm")]
206 pub fn default_stat() -> libc::stat {
231 #[cfg(target_arch = "mips")]
235 pub fn default_stat() -> libc::stat {
261 #[cfg(target_arch = "x86_64")]
265 pub fn default_stat() -> libc::stat {
290 #[cfg(target_os = "freebsd")]
292 #[cfg(target_arch = "x86_64")]
296 pub fn default_stat() -> libc::stat {
318 st_birthtime_nsec: 0,
325 #[cfg(target_os = "macos")]
330 pub fn default_stat() -> libc::stat {
346 st_birthtime_nsec: 0,
359 #[cfg(target_os = "win32")]
363 pub fn default_stat() -> libc::stat {
381 #[cfg(target_os = "win32")]
383 pub fn stat(&self) -> Option<libc::stat> {
384 #[fixed_stack_segment]; #[inline(never)];
385 do self.with_c_str |buf| {
386 let mut st = stat::arch::default_stat();
387 match unsafe { libc::stat(buf, &mut st) } {
394 pub fn exists(&self) -> bool {
401 pub fn get_size(&self) -> Option<i64> {
404 Some(ref st) => Some(st.st_size as i64),
408 pub fn get_mode(&self) -> Option<uint> {
411 Some(ref st) => Some(st.st_mode as uint),
416 #[cfg(not(target_os = "win32"))]
418 pub fn stat(&self) -> Option<libc::stat> {
419 #[fixed_stack_segment]; #[inline(never)];
420 do self.with_c_str |buf| {
421 let mut st = stat::arch::default_stat();
422 match unsafe { libc::stat(buf as *libc::c_char, &mut st) } {
429 pub fn exists(&self) -> bool {
436 pub fn get_size(&self) -> Option<i64> {
439 Some(ref st) => Some(st.st_size as i64),
443 pub fn get_mode(&self) -> Option<uint> {
446 Some(ref st) => Some(st.st_mode as uint),
450 /// Execute a function on p as well as all of its ancestors
451 pub fn each_parent(&self, f: &fn(&Path)) {
452 if !self.components.is_empty() {
454 self.pop().each_parent(f);
460 #[cfg(target_os = "freebsd")]
461 #[cfg(target_os = "linux")]
462 #[cfg(target_os = "macos")]
464 pub fn get_atime(&self) -> Option<(i64, int)> {
468 Some((st.st_atime as i64,
469 st.st_atime_nsec as int))
474 pub fn get_mtime(&self) -> Option<(i64, int)> {
478 Some((st.st_mtime as i64,
479 st.st_mtime_nsec as int))
484 pub fn get_ctime(&self) -> Option<(i64, int)> {
488 Some((st.st_ctime as i64,
489 st.st_ctime_nsec as int))
497 pub fn lstat(&self) -> Option<libc::stat> {
498 #[fixed_stack_segment]; #[inline(never)];
499 do self.with_c_str |buf| {
500 let mut st = stat::arch::default_stat();
501 match unsafe { libc::lstat(buf, &mut st) } {
509 #[cfg(target_os = "freebsd")]
510 #[cfg(target_os = "macos")]
512 pub fn get_birthtime(&self) -> Option<(i64, int)> {
516 Some((st.st_birthtime as i64,
517 st.st_birthtime_nsec as int))
523 #[cfg(target_os = "win32")]
525 pub fn get_atime(&self) -> Option<(i64, int)> {
529 Some((st.st_atime as i64, 0))
534 pub fn get_mtime(&self) -> Option<(i64, int)> {
538 Some((st.st_mtime as i64, 0))
543 pub fn get_ctime(&self) -> Option<(i64, int)> {
547 Some((st.st_ctime as i64, 0))
552 /// Execute a function on p as well as all of its ancestors
553 pub fn each_parent(&self, f: &fn(&Path)) {
554 if !self.components.is_empty() {
556 self.pop().each_parent(f);
561 impl ToStr for PosixPath {
562 fn to_str(&self) -> ~str {
564 if self.is_absolute {
567 s + self.components.connect("/")
571 impl ToCStr for PosixPath {
572 fn to_c_str(&self) -> c_str::CString {
573 self.to_str().to_c_str()
576 unsafe fn to_c_str_unchecked(&self) -> c_str::CString {
577 self.to_str().to_c_str_unchecked()
581 // FIXME (#3227): when default methods in traits are working, de-duplicate
582 // PosixPath and WindowsPath, most of their methods are common.
583 impl GenericPath for PosixPath {
584 fn from_str(s: &str) -> PosixPath {
585 let components = s.split_iter('/')
586 .filter_map(|s| if s.is_empty() {None} else {Some(s.to_owned())})
588 let is_absolute = (s.len() != 0 && s[0] == '/' as u8);
590 is_absolute: is_absolute,
591 components: components,
595 fn dirname(&self) -> ~str {
596 let s = self.dir_path().to_str();
603 fn filename(&self) -> Option<~str> {
604 match self.components.len() {
606 n => Some(self.components[n - 1].clone()),
610 fn filestem(&self) -> Option<~str> {
611 match self.filename() {
615 Some(p) => Some(f.slice_to(p).to_owned()),
616 None => Some((*f).clone()),
622 fn filetype(&self) -> Option<~str> {
623 match self.filename() {
627 Some(p) if p < f.len() => Some(f.slice_from(p).to_owned()),
634 fn with_dirname(&self, d: &str) -> PosixPath {
635 let dpath = PosixPath(d);
636 match self.filename() {
637 Some(ref f) => dpath.push(*f),
642 fn with_filename(&self, f: &str) -> PosixPath {
643 assert!(!f.iter().all(posix::is_sep));
644 self.dir_path().push(f)
647 fn with_filestem(&self, s: &str) -> PosixPath {
648 match self.filetype() {
649 None => self.with_filename(s),
650 Some(ref t) => self.with_filename(s.to_owned() + *t),
654 fn with_filetype(&self, t: &str) -> PosixPath {
655 match (t.len(), self.filestem()) {
656 (0, None) => (*self).clone(),
657 (0, Some(ref s)) => self.with_filename(*s),
658 (_, None) => self.with_filename(fmt!(".%s", t)),
659 (_, Some(ref s)) => self.with_filename(fmt!("%s.%s", *s, t)),
663 fn dir_path(&self) -> PosixPath {
664 match self.components.len() {
665 0 => (*self).clone(),
670 fn file_path(&self) -> PosixPath {
671 let cs = match self.filename() {
673 Some(ref f) => ~[(*f).clone()]
681 fn push_rel(&self, other: &PosixPath) -> PosixPath {
682 assert!(!other.is_absolute);
683 self.push_many(other.components)
686 fn unsafe_join(&self, other: &PosixPath) -> PosixPath {
687 if other.is_absolute {
690 components: other.components.clone(),
697 fn is_restricted(&self) -> bool {
701 fn push_many<S: Str>(&self, cs: &[S]) -> PosixPath {
702 let mut v = self.components.clone();
704 for s in e.as_slice().split_iter(posix::is_sep) {
711 is_absolute: self.is_absolute,
716 fn push(&self, s: &str) -> PosixPath {
717 let mut v = self.components.clone();
718 for s in s.split_iter(posix::is_sep) {
729 fn pop(&self) -> PosixPath {
730 let mut cs = self.components.clone();
735 is_absolute: self.is_absolute,
740 fn normalize(&self) -> PosixPath {
742 is_absolute: self.is_absolute,
743 components: normalize(self.components),
747 fn is_absolute(&self) -> bool {
751 fn is_ancestor_of(&self, other: &PosixPath) -> bool {
752 debug!("%s / %s %? %?", self.to_str(), other.to_str(), self.is_absolute,
753 self.components.len());
755 (!other.components.is_empty() && !(self.components.is_empty() && !self.is_absolute) &&
756 self.is_ancestor_of(&other.pop()))
759 fn components(self) -> ~[~str] { self.components }
763 impl ToStr for WindowsPath {
764 fn to_str(&self) -> ~str {
780 if self.is_absolute {
783 s + self.components.connect("\\")
787 impl c_str::ToCStr for WindowsPath {
788 fn to_c_str(&self) -> c_str::CString {
789 self.to_str().to_c_str()
792 unsafe fn to_c_str_unchecked(&self) -> c_str::CString {
793 self.to_str().to_c_str_unchecked()
797 impl GenericPath for WindowsPath {
798 fn from_str(s: &str) -> WindowsPath {
804 windows::extract_drive_prefix(s),
805 windows::extract_unc_prefix(s),
807 (Some((ref d, ref r)), _) => {
809 device = Some((*d).clone());
812 (None, Some((ref h, ref r))) => {
813 host = Some((*h).clone());
824 let components = rest.split_iter(windows::is_sep)
825 .filter_map(|s| if s.is_empty() {None} else {Some(s.to_owned())})
828 let is_absolute = (rest.len() != 0 && windows::is_sep(rest[0] as char));
832 is_absolute: is_absolute,
833 components: components,
837 fn dirname(&self) -> ~str {
838 let s = self.dir_path().to_str();
845 fn filename(&self) -> Option<~str> {
846 match self.components.len() {
848 n => Some(self.components[n - 1].clone()),
852 fn filestem(&self) -> Option<~str> {
853 match self.filename() {
857 Some(p) => Some(f.slice_to(p).to_owned()),
858 None => Some((*f).clone()),
864 fn filetype(&self) -> Option<~str> {
865 match self.filename() {
869 Some(p) if p < f.len() => Some(f.slice_from(p).to_owned()),
876 fn with_dirname(&self, d: &str) -> WindowsPath {
877 let dpath = WindowsPath(d);
878 match self.filename() {
879 Some(ref f) => dpath.push(*f),
884 fn with_filename(&self, f: &str) -> WindowsPath {
885 assert!(! f.iter().all(windows::is_sep));
886 self.dir_path().push(f)
889 fn with_filestem(&self, s: &str) -> WindowsPath {
890 match self.filetype() {
891 None => self.with_filename(s),
892 Some(ref t) => self.with_filename(s.to_owned() + *t),
896 fn with_filetype(&self, t: &str) -> WindowsPath {
897 match (t.len(), self.filestem()) {
898 (0, None) => (*self).clone(),
899 (0, Some(ref s)) => self.with_filename(*s),
900 (_, None) => self.with_filename(fmt!(".%s", t)),
901 (_, Some(ref s)) => self.with_filename(fmt!("%s.%s", *s, t)),
905 fn dir_path(&self) -> WindowsPath {
906 match self.components.len() {
907 0 => (*self).clone(),
912 fn file_path(&self) -> WindowsPath {
917 components: match self.filename() {
919 Some(ref f) => ~[(*f).clone()],
924 fn push_rel(&self, other: &WindowsPath) -> WindowsPath {
925 assert!(!other.is_absolute);
926 self.push_many(other.components)
929 fn unsafe_join(&self, other: &WindowsPath) -> WindowsPath {
930 /* rhs not absolute is simple push */
931 if !other.is_absolute {
932 return self.push_many(other.components);
935 /* if rhs has a host set, then the whole thing wins */
939 host: Some((*host).clone()),
940 device: other.device.clone(),
942 components: other.components.clone(),
948 /* if rhs has a device set, then a part wins */
950 Some(ref device) => {
953 device: Some((*device).clone()),
955 components: other.components.clone(),
961 /* fallback: host and device of lhs win, but the
962 whole path of the right */
964 host: self.host.clone(),
965 device: self.device.clone(),
966 is_absolute: self.is_absolute || other.is_absolute,
967 components: other.components.clone(),
971 fn is_restricted(&self) -> bool {
972 match self.filestem() {
974 // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use
975 // to_ascii_move and to_str_move to not do a unnecessary copy.
976 match stem.to_ascii().to_lower().to_str_ascii() {
977 ~"con" | ~"aux" | ~"com1" | ~"com2" | ~"com3" | ~"com4" |
978 ~"lpt1" | ~"lpt2" | ~"lpt3" | ~"prn" | ~"nul" => true,
986 fn push_many<S: Str>(&self, cs: &[S]) -> WindowsPath {
987 let mut v = self.components.clone();
989 for s in e.as_slice().split_iter(windows::is_sep) {
995 // tedious, but as-is, we can't use ..self
997 host: self.host.clone(),
998 device: self.device.clone(),
999 is_absolute: self.is_absolute,
1004 fn push(&self, s: &str) -> WindowsPath {
1005 let mut v = self.components.clone();
1006 for s in s.split_iter(windows::is_sep) {
1008 v.push(s.to_owned())
1011 WindowsPath { components: v, ..(*self).clone() }
1014 fn pop(&self) -> WindowsPath {
1015 let mut cs = self.components.clone();
1020 host: self.host.clone(),
1021 device: self.device.clone(),
1022 is_absolute: self.is_absolute,
1027 fn normalize(&self) -> WindowsPath {
1029 host: self.host.clone(),
1030 device: match self.device {
1033 // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use
1034 // to_ascii_move and to_str_move to not do a unnecessary copy.
1035 Some(ref device) => Some(device.to_ascii().to_upper().to_str_ascii())
1037 is_absolute: self.is_absolute,
1038 components: normalize(self.components)
1042 fn is_absolute(&self) -> bool {
1046 fn is_ancestor_of(&self, other: &WindowsPath) -> bool {
1048 (!other.components.is_empty() && !(self.components.is_empty() && !self.is_absolute) &&
1049 self.is_ancestor_of(&other.pop()))
1052 fn components(self) -> ~[~str] { self.components }
1055 pub fn normalize(components: &[~str]) -> ~[~str] {
1057 for c in components.iter() {
1058 if *c == ~"." && components.len() > 1 { loop; }
1059 if *c == ~"" { loop; }
1060 if *c == ~".." && cs.len() != 0 {
1064 cs.push((*c).clone());
1069 // Various posix helpers.
1073 pub fn is_sep(u: char) -> bool {
1079 // Various windows helpers.
1082 use option::{None, Option, Some};
1085 pub fn is_sep(u: char) -> bool {
1086 u == '/' || u == '\\'
1089 pub fn extract_unc_prefix(s: &str) -> Option<(~str,~str)> {
1091 (s[0] == '\\' as u8 || s[0] == '/' as u8) &&
1095 if is_sep(s[i] as char) {
1096 let pre = s.slice(2, i).to_owned();
1097 let rest = s.slice(i, s.len()).to_owned();
1098 return Some((pre, rest));
1106 pub fn extract_drive_prefix(s: &str) -> Option<(~str,~str)> {
1107 #[fixed_stack_segment]; #[inline(never)];
1111 libc::isalpha(s[0] as libc::c_int) != 0 &&
1112 s[1] == ':' as u8) {
1113 let rest = if s.len() == 2 {
1116 s.slice(2, s.len()).to_owned()
1118 return Some((s.slice(0,1).to_owned(), rest));
1127 use option::{None, Some};
1128 use path::{PosixPath, WindowsPath, windows};
1131 fn test_double_slash_collapsing() {
1132 let path = PosixPath("tmp/");
1133 let path = path.push("/hmm");
1134 let path = path.normalize();
1135 assert_eq!(~"tmp/hmm", path.to_str());
1137 let path = WindowsPath("tmp/");
1138 let path = path.push("/hmm");
1139 let path = path.normalize();
1140 assert_eq!(~"tmp\\hmm", path.to_str());
1144 fn test_filetype_foo_bar() {
1145 let wp = PosixPath("foo.bar");
1146 assert_eq!(wp.filetype(), Some(~".bar"));
1148 let wp = WindowsPath("foo.bar");
1149 assert_eq!(wp.filetype(), Some(~".bar"));
1153 fn test_filetype_foo() {
1154 let wp = PosixPath("foo");
1155 assert_eq!(wp.filetype(), None);
1157 let wp = WindowsPath("foo");
1158 assert_eq!(wp.filetype(), None);
1162 fn test_posix_paths() {
1163 fn t(wp: &PosixPath, s: &str) {
1164 let ss = wp.to_str();
1165 let sss = s.to_owned();
1167 debug!("got %s", ss);
1168 debug!("expected %s", sss);
1169 assert_eq!(ss, sss);
1173 t(&(PosixPath("hi")), "hi");
1174 t(&(PosixPath("/lib")), "/lib");
1175 t(&(PosixPath("hi/there")), "hi/there");
1176 t(&(PosixPath("hi/there.txt")), "hi/there.txt");
1178 t(&(PosixPath("hi/there.txt")), "hi/there.txt");
1179 t(&(PosixPath("hi/there.txt")
1180 .with_filetype("")), "hi/there");
1182 t(&(PosixPath("/a/b/c/there.txt")
1183 .with_dirname("hi")), "hi/there.txt");
1185 t(&(PosixPath("hi/there.txt")
1186 .with_dirname(".")), "./there.txt");
1188 t(&(PosixPath("a/b/c")
1189 .push("..")), "a/b/c/..");
1191 t(&(PosixPath("there.txt")
1192 .with_filetype("o")), "there.o");
1194 t(&(PosixPath("hi/there.txt")
1195 .with_filetype("o")), "hi/there.o");
1197 t(&(PosixPath("hi/there.txt")
1199 .with_dirname("/usr/lib")),
1200 "/usr/lib/there.o");
1202 t(&(PosixPath("hi/there.txt")
1204 .with_dirname("/usr/lib/")),
1205 "/usr/lib/there.o");
1207 t(&(PosixPath("hi/there.txt")
1209 .with_dirname("/usr//lib//")),
1210 "/usr/lib/there.o");
1212 t(&(PosixPath("/usr/bin/rust")
1213 .push_many([~"lib", ~"thingy.so"])
1214 .with_filestem("librustc")),
1215 "/usr/bin/rust/lib/librustc.so");
1220 fn test_posix_push_with_backslash() {
1221 let a = PosixPath("/aaa/bbb");
1222 let b = a.push("x\\y"); // \ is not a file separator for posix paths
1223 assert_eq!(a.components.len(), 2);
1224 assert_eq!(b.components.len(), 3);
1228 fn test_normalize() {
1229 fn t(wp: &PosixPath, s: &str) {
1230 let ss = wp.to_str();
1231 let sss = s.to_owned();
1233 debug!("got %s", ss);
1234 debug!("expected %s", sss);
1235 assert_eq!(ss, sss);
1239 t(&(PosixPath("hi/there.txt")
1240 .with_dirname(".").normalize()), "there.txt");
1242 t(&(PosixPath("a/b/../c/././/../foo.txt/").normalize()),
1245 t(&(PosixPath("a/b/c")
1246 .push("..").normalize()), "a/b");
1250 fn test_extract_unc_prefixes() {
1251 assert!(windows::extract_unc_prefix("\\\\").is_none());
1252 assert!(windows::extract_unc_prefix("//").is_none());
1253 assert!(windows::extract_unc_prefix("\\\\hi").is_none());
1254 assert!(windows::extract_unc_prefix("//hi").is_none());
1255 assert!(windows::extract_unc_prefix("\\\\hi\\") ==
1256 Some((~"hi", ~"\\")));
1257 assert!(windows::extract_unc_prefix("//hi\\") ==
1258 Some((~"hi", ~"\\")));
1259 assert!(windows::extract_unc_prefix("\\\\hi\\there") ==
1260 Some((~"hi", ~"\\there")));
1261 assert!(windows::extract_unc_prefix("//hi/there") ==
1262 Some((~"hi", ~"/there")));
1263 assert!(windows::extract_unc_prefix(
1264 "\\\\hi\\there\\friends.txt") ==
1265 Some((~"hi", ~"\\there\\friends.txt")));
1266 assert!(windows::extract_unc_prefix(
1267 "//hi\\there\\friends.txt") ==
1268 Some((~"hi", ~"\\there\\friends.txt")));
1272 fn test_extract_drive_prefixes() {
1273 assert!(windows::extract_drive_prefix("c").is_none());
1274 assert!(windows::extract_drive_prefix("c:") ==
1276 assert!(windows::extract_drive_prefix("d:") ==
1278 assert!(windows::extract_drive_prefix("z:") ==
1280 assert!(windows::extract_drive_prefix("c:\\hi") ==
1281 Some((~"c", ~"\\hi")));
1282 assert!(windows::extract_drive_prefix("d:hi") ==
1283 Some((~"d", ~"hi")));
1284 assert!(windows::extract_drive_prefix("c:hi\\there.txt") ==
1285 Some((~"c", ~"hi\\there.txt")));
1286 assert!(windows::extract_drive_prefix("c:\\hi\\there.txt") ==
1287 Some((~"c", ~"\\hi\\there.txt")));
1291 fn test_windows_paths() {
1292 fn t(wp: &WindowsPath, s: &str) {
1293 let ss = wp.to_str();
1294 let sss = s.to_owned();
1296 debug!("got %s", ss);
1297 debug!("expected %s", sss);
1298 assert_eq!(ss, sss);
1302 t(&(WindowsPath("hi")), "hi");
1303 t(&(WindowsPath("hi/there")), "hi\\there");
1304 t(&(WindowsPath("hi/there.txt")), "hi\\there.txt");
1306 t(&(WindowsPath("there.txt")
1307 .with_filetype("o")), "there.o");
1309 t(&(WindowsPath("hi/there.txt")
1310 .with_filetype("o")), "hi\\there.o");
1312 t(&(WindowsPath("hi/there.txt")
1314 .with_dirname("c:\\program files A")),
1315 "c:\\program files A\\there.o");
1317 t(&(WindowsPath("hi/there.txt")
1319 .with_dirname("c:\\program files B\\")),
1320 "c:\\program files B\\there.o");
1322 t(&(WindowsPath("hi/there.txt")
1324 .with_dirname("c:\\program files C\\/")),
1325 "c:\\program files C\\there.o");
1327 t(&(WindowsPath("c:\\program files (x86)\\rust")
1328 .push_many([~"lib", ~"thingy.dll"])
1329 .with_filename("librustc.dll")),
1330 "c:\\program files (x86)\\rust\\lib\\librustc.dll");
1332 t(&(WindowsPath("\\\\computer\\share")
1333 .unsafe_join(&WindowsPath("\\a"))),
1336 t(&(WindowsPath("//computer/share")
1337 .unsafe_join(&WindowsPath("\\a"))),
1340 t(&(WindowsPath("//computer/share")
1341 .unsafe_join(&WindowsPath("\\\\computer\\share"))),
1342 "\\\\computer\\share");
1344 t(&(WindowsPath("C:/whatever")
1345 .unsafe_join(&WindowsPath("//computer/share/a/b"))),
1346 "\\\\computer\\share\\a\\b");
1348 t(&(WindowsPath("C:")
1349 .unsafe_join(&WindowsPath("D:/foo"))),
1352 t(&(WindowsPath("C:")
1353 .unsafe_join(&WindowsPath("B"))),
1356 t(&(WindowsPath("C:")
1357 .unsafe_join(&WindowsPath("/foo"))),
1360 t(&(WindowsPath("C:\\")
1361 .unsafe_join(&WindowsPath("\\bar"))),
1365 .unsafe_join(&WindowsPath(""))),
1369 .unsafe_join(&WindowsPath("a"))),
1373 .unsafe_join(&WindowsPath("C:\\a"))),
1376 t(&(WindowsPath("c:\\foo")
1382 fn test_windows_path_restrictions() {
1383 assert_eq!(WindowsPath("hi").is_restricted(), false);
1384 assert_eq!(WindowsPath("C:\\NUL").is_restricted(), true);
1385 assert_eq!(WindowsPath("C:\\COM1.TXT").is_restricted(), true);
1386 assert_eq!(WindowsPath("c:\\prn.exe").is_restricted(), true);
1390 fn test_is_ancestor_of() {
1391 assert!(&PosixPath("/a/b").is_ancestor_of(&PosixPath("/a/b/c/d")));
1392 assert!(!&PosixPath("/a/b/c/d").is_ancestor_of(&PosixPath("/a/b")));
1393 assert!(!&PosixPath("/a/b").is_ancestor_of(&PosixPath("/c/d")));
1394 assert!(&PosixPath("/a/b").is_ancestor_of(&PosixPath("/a/b/c/d")));
1395 assert!(&PosixPath("/").is_ancestor_of(&PosixPath("/a/b/c")));
1396 assert!(!&PosixPath("/").is_ancestor_of(&PosixPath("")));
1397 assert!(!&PosixPath("/a/b/c").is_ancestor_of(&PosixPath("")));
1398 assert!(!&PosixPath("").is_ancestor_of(&PosixPath("/a/b/c")));
1400 assert!(&WindowsPath("C:\\a\\b").is_ancestor_of(&WindowsPath("C:\\a\\b\\c\\d")));
1401 assert!(!&WindowsPath("C:\\a\\b\\c\\d").is_ancestor_of(&WindowsPath("C:\\a\\b")));
1402 assert!(!&WindowsPath("C:\\a\\b").is_ancestor_of(&WindowsPath("C:\\c\\d")));
1403 assert!(&WindowsPath("C:\\a\\b").is_ancestor_of(&WindowsPath("C:\\a\\b\\c\\d")));
1404 assert!(&WindowsPath("C:\\").is_ancestor_of(&WindowsPath("C:\\a\\b\\c")));
1405 assert!(!&WindowsPath("C:\\").is_ancestor_of(&WindowsPath("")));
1406 assert!(!&WindowsPath("C:\\a\\b\\c").is_ancestor_of(&WindowsPath("")));
1407 assert!(!&WindowsPath("").is_ancestor_of(&WindowsPath("C:\\a\\b\\c")));
1412 fn test_relative_to1() {
1413 let p1 = PosixPath("/usr/bin/rustc");
1414 let p2 = PosixPath("/usr/lib/mylib");
1415 let res = p1.get_relative_to(&p2);
1416 assert_eq!(res, PosixPath("../lib"));
1418 let p1 = WindowsPath("C:\\usr\\bin\\rustc");
1419 let p2 = WindowsPath("C:\\usr\\lib\\mylib");
1420 let res = p1.get_relative_to(&p2);
1421 assert_eq!(res, WindowsPath("..\\lib"));
1426 fn test_relative_to2() {
1427 let p1 = PosixPath("/usr/bin/rustc");
1428 let p2 = PosixPath("/usr/bin/../lib/mylib");
1429 let res = p1.get_relative_to(&p2);
1430 assert_eq!(res, PosixPath("../lib"));
1432 let p1 = WindowsPath("C:\\usr\\bin\\rustc");
1433 let p2 = WindowsPath("C:\\usr\\bin\\..\\lib\\mylib");
1434 let res = p1.get_relative_to(&p2);
1435 assert_eq!(res, WindowsPath("..\\lib"));
1439 fn test_relative_to3() {
1440 let p1 = PosixPath("/usr/bin/whatever/rustc");
1441 let p2 = PosixPath("/usr/lib/whatever/mylib");
1442 let res = p1.get_relative_to(&p2);
1443 assert_eq!(res, PosixPath("../../lib/whatever"));
1445 let p1 = WindowsPath("C:\\usr\\bin\\whatever\\rustc");
1446 let p2 = WindowsPath("C:\\usr\\lib\\whatever\\mylib");
1447 let res = p1.get_relative_to(&p2);
1448 assert_eq!(res, WindowsPath("..\\..\\lib\\whatever"));
1453 fn test_relative_to4() {
1454 let p1 = PosixPath("/usr/bin/whatever/../rustc");
1455 let p2 = PosixPath("/usr/lib/whatever/mylib");
1456 let res = p1.get_relative_to(&p2);
1457 assert_eq!(res, PosixPath("../lib/whatever"));
1459 let p1 = WindowsPath("C:\\usr\\bin\\whatever\\..\\rustc");
1460 let p2 = WindowsPath("C:\\usr\\lib\\whatever\\mylib");
1461 let res = p1.get_relative_to(&p2);
1462 assert_eq!(res, WindowsPath("..\\lib\\whatever"));
1467 fn test_relative_to5() {
1468 let p1 = PosixPath("/usr/bin/whatever/../rustc");
1469 let p2 = PosixPath("/usr/lib/whatever/../mylib");
1470 let res = p1.get_relative_to(&p2);
1471 assert_eq!(res, PosixPath("../lib"));
1473 let p1 = WindowsPath("C:\\usr\\bin/whatever\\..\\rustc");
1474 let p2 = WindowsPath("C:\\usr\\lib\\whatever\\..\\mylib");
1475 let res = p1.get_relative_to(&p2);
1476 assert_eq!(res, WindowsPath("..\\lib"));
1480 fn test_relative_to6() {
1481 let p1 = PosixPath("/1");
1482 let p2 = PosixPath("/2/3");
1483 let res = p1.get_relative_to(&p2);
1484 assert_eq!(res, PosixPath("2"));
1486 let p1 = WindowsPath("C:\\1");
1487 let p2 = WindowsPath("C:\\2\\3");
1488 let res = p1.get_relative_to(&p2);
1489 assert_eq!(res, WindowsPath("2"));
1494 fn test_relative_to7() {
1495 let p1 = PosixPath("/1/2");
1496 let p2 = PosixPath("/3");
1497 let res = p1.get_relative_to(&p2);
1498 assert_eq!(res, PosixPath(".."));
1500 let p1 = WindowsPath("C:\\1\\2");
1501 let p2 = WindowsPath("C:\\3");
1502 let res = p1.get_relative_to(&p2);
1503 assert_eq!(res, WindowsPath(".."));
1508 fn test_relative_to8() {
1509 let p1 = PosixPath("/home/brian/Dev/rust/build/").push_rel(
1510 &PosixPath("stage2/lib/rustc/i686-unknown-linux-gnu/lib/librustc.so"));
1511 let p2 = PosixPath("/home/brian/Dev/rust/build/stage2/bin/..").push_rel(
1512 &PosixPath("lib/rustc/i686-unknown-linux-gnu/lib/libstd.so"));
1513 let res = p1.get_relative_to(&p2);
1514 debug!("test_relative_to8: %s vs. %s",
1516 PosixPath(".").to_str());
1517 assert_eq!(res, PosixPath("."));
1519 let p1 = WindowsPath("C:\\home\\brian\\Dev\\rust\\build\\").push_rel(
1520 &WindowsPath("stage2\\lib\\rustc\\i686-unknown-linux-gnu\\lib\\librustc.so"));
1521 let p2 = WindowsPath("\\home\\brian\\Dev\\rust\\build\\stage2\\bin\\..").push_rel(
1522 &WindowsPath("lib\\rustc\\i686-unknown-linux-gnu\\lib\\libstd.so"));
1523 let res = p1.get_relative_to(&p2);
1524 debug!("test_relative_to8: %s vs. %s",
1526 WindowsPath(".").to_str());
1527 assert_eq!(res, WindowsPath("."));