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 do self.with_c_str |buf| {
385 let mut st = stat::arch::default_stat();
386 match unsafe { libc::stat(buf, &mut st) } {
393 pub fn exists(&self) -> bool {
400 pub fn get_size(&self) -> Option<i64> {
403 Some(ref st) => Some(st.st_size as i64),
407 pub fn get_mode(&self) -> Option<uint> {
410 Some(ref st) => Some(st.st_mode as uint),
415 #[cfg(not(target_os = "win32"))]
417 pub fn stat(&self) -> Option<libc::stat> {
418 do self.with_c_str |buf| {
419 let mut st = stat::arch::default_stat();
420 match unsafe { libc::stat(buf as *libc::c_char, &mut st) } {
427 pub fn exists(&self) -> bool {
434 pub fn get_size(&self) -> Option<i64> {
437 Some(ref st) => Some(st.st_size as i64),
441 pub fn get_mode(&self) -> Option<uint> {
444 Some(ref st) => Some(st.st_mode as uint),
448 /// Execute a function on p as well as all of its ancestors
449 pub fn each_parent(&self, f: &fn(&Path)) {
450 if !self.components.is_empty() {
452 self.pop().each_parent(f);
458 #[cfg(target_os = "freebsd")]
459 #[cfg(target_os = "linux")]
460 #[cfg(target_os = "macos")]
462 pub fn get_atime(&self) -> Option<(i64, int)> {
466 Some((st.st_atime as i64,
467 st.st_atime_nsec as int))
472 pub fn get_mtime(&self) -> Option<(i64, int)> {
476 Some((st.st_mtime as i64,
477 st.st_mtime_nsec as int))
482 pub fn get_ctime(&self) -> Option<(i64, int)> {
486 Some((st.st_ctime as i64,
487 st.st_ctime_nsec as int))
495 pub fn lstat(&self) -> Option<libc::stat> {
496 do self.with_c_str |buf| {
497 let mut st = stat::arch::default_stat();
498 match unsafe { libc::lstat(buf, &mut st) } {
506 #[cfg(target_os = "freebsd")]
507 #[cfg(target_os = "macos")]
509 pub fn get_birthtime(&self) -> Option<(i64, int)> {
513 Some((st.st_birthtime as i64,
514 st.st_birthtime_nsec as int))
520 #[cfg(target_os = "win32")]
522 pub fn get_atime(&self) -> Option<(i64, int)> {
526 Some((st.st_atime as i64, 0))
531 pub fn get_mtime(&self) -> Option<(i64, int)> {
535 Some((st.st_mtime as i64, 0))
540 pub fn get_ctime(&self) -> Option<(i64, int)> {
544 Some((st.st_ctime as i64, 0))
549 /// Execute a function on p as well as all of its ancestors
550 pub fn each_parent(&self, f: &fn(&Path)) {
551 if !self.components.is_empty() {
553 self.pop().each_parent(f);
558 impl ToStr for PosixPath {
559 fn to_str(&self) -> ~str {
561 if self.is_absolute {
564 s + self.components.connect("/")
568 impl ToCStr for PosixPath {
569 fn to_c_str(&self) -> c_str::CString {
570 self.to_str().to_c_str()
573 unsafe fn to_c_str_unchecked(&self) -> c_str::CString {
574 self.to_str().to_c_str_unchecked()
578 // FIXME (#3227): when default methods in traits are working, de-duplicate
579 // PosixPath and WindowsPath, most of their methods are common.
580 impl GenericPath for PosixPath {
581 fn from_str(s: &str) -> PosixPath {
582 let components = s.split_iter('/')
583 .filter_map(|s| if s.is_empty() {None} else {Some(s.to_owned())})
585 let is_absolute = (s.len() != 0 && s[0] == '/' as u8);
587 is_absolute: is_absolute,
588 components: components,
592 fn dirname(&self) -> ~str {
593 let s = self.dir_path().to_str();
600 fn filename(&self) -> Option<~str> {
601 match self.components.len() {
603 n => Some(self.components[n - 1].clone()),
607 fn filestem(&self) -> Option<~str> {
608 match self.filename() {
612 Some(p) => Some(f.slice_to(p).to_owned()),
613 None => Some((*f).clone()),
619 fn filetype(&self) -> Option<~str> {
620 match self.filename() {
624 Some(p) if p < f.len() => Some(f.slice_from(p).to_owned()),
631 fn with_dirname(&self, d: &str) -> PosixPath {
632 let dpath = PosixPath(d);
633 match self.filename() {
634 Some(ref f) => dpath.push(*f),
639 fn with_filename(&self, f: &str) -> PosixPath {
640 assert!(!f.iter().all(posix::is_sep));
641 self.dir_path().push(f)
644 fn with_filestem(&self, s: &str) -> PosixPath {
645 match self.filetype() {
646 None => self.with_filename(s),
647 Some(ref t) => self.with_filename(s.to_owned() + *t),
651 fn with_filetype(&self, t: &str) -> PosixPath {
652 match (t.len(), self.filestem()) {
653 (0, None) => (*self).clone(),
654 (0, Some(ref s)) => self.with_filename(*s),
655 (_, None) => self.with_filename(fmt!(".%s", t)),
656 (_, Some(ref s)) => self.with_filename(fmt!("%s.%s", *s, t)),
660 fn dir_path(&self) -> PosixPath {
661 match self.components.len() {
662 0 => (*self).clone(),
667 fn file_path(&self) -> PosixPath {
668 let cs = match self.filename() {
670 Some(ref f) => ~[(*f).clone()]
678 fn push_rel(&self, other: &PosixPath) -> PosixPath {
679 assert!(!other.is_absolute);
680 self.push_many(other.components)
683 fn unsafe_join(&self, other: &PosixPath) -> PosixPath {
684 if other.is_absolute {
687 components: other.components.clone(),
694 fn is_restricted(&self) -> bool {
698 fn push_many<S: Str>(&self, cs: &[S]) -> PosixPath {
699 let mut v = self.components.clone();
701 for s in e.as_slice().split_iter(posix::is_sep) {
708 is_absolute: self.is_absolute,
713 fn push(&self, s: &str) -> PosixPath {
714 let mut v = self.components.clone();
715 for s in s.split_iter(posix::is_sep) {
726 fn pop(&self) -> PosixPath {
727 let mut cs = self.components.clone();
732 is_absolute: self.is_absolute,
737 fn normalize(&self) -> PosixPath {
739 is_absolute: self.is_absolute,
740 components: normalize(self.components),
744 fn is_absolute(&self) -> bool {
748 fn is_ancestor_of(&self, other: &PosixPath) -> bool {
749 debug!("%s / %s %? %?", self.to_str(), other.to_str(), self.is_absolute,
750 self.components.len());
752 (!other.components.is_empty() && !(self.components.is_empty() && !self.is_absolute) &&
753 self.is_ancestor_of(&other.pop()))
756 fn components(self) -> ~[~str] { self.components }
760 impl ToStr for WindowsPath {
761 fn to_str(&self) -> ~str {
777 if self.is_absolute {
780 s + self.components.connect("\\")
784 impl c_str::ToCStr for WindowsPath {
785 fn to_c_str(&self) -> c_str::CString {
786 self.to_str().to_c_str()
789 unsafe fn to_c_str_unchecked(&self) -> c_str::CString {
790 self.to_str().to_c_str_unchecked()
794 impl GenericPath for WindowsPath {
795 fn from_str(s: &str) -> WindowsPath {
801 windows::extract_drive_prefix(s),
802 windows::extract_unc_prefix(s),
804 (Some((ref d, ref r)), _) => {
806 device = Some((*d).clone());
809 (None, Some((ref h, ref r))) => {
810 host = Some((*h).clone());
821 let components = rest.split_iter(windows::is_sep)
822 .filter_map(|s| if s.is_empty() {None} else {Some(s.to_owned())})
825 let is_absolute = (rest.len() != 0 && windows::is_sep(rest[0] as char));
829 is_absolute: is_absolute,
830 components: components,
834 fn dirname(&self) -> ~str {
835 let s = self.dir_path().to_str();
842 fn filename(&self) -> Option<~str> {
843 match self.components.len() {
845 n => Some(self.components[n - 1].clone()),
849 fn filestem(&self) -> Option<~str> {
850 match self.filename() {
854 Some(p) => Some(f.slice_to(p).to_owned()),
855 None => Some((*f).clone()),
861 fn filetype(&self) -> Option<~str> {
862 match self.filename() {
866 Some(p) if p < f.len() => Some(f.slice_from(p).to_owned()),
873 fn with_dirname(&self, d: &str) -> WindowsPath {
874 let dpath = WindowsPath(d);
875 match self.filename() {
876 Some(ref f) => dpath.push(*f),
881 fn with_filename(&self, f: &str) -> WindowsPath {
882 assert!(! f.iter().all(windows::is_sep));
883 self.dir_path().push(f)
886 fn with_filestem(&self, s: &str) -> WindowsPath {
887 match self.filetype() {
888 None => self.with_filename(s),
889 Some(ref t) => self.with_filename(s.to_owned() + *t),
893 fn with_filetype(&self, t: &str) -> WindowsPath {
894 match (t.len(), self.filestem()) {
895 (0, None) => (*self).clone(),
896 (0, Some(ref s)) => self.with_filename(*s),
897 (_, None) => self.with_filename(fmt!(".%s", t)),
898 (_, Some(ref s)) => self.with_filename(fmt!("%s.%s", *s, t)),
902 fn dir_path(&self) -> WindowsPath {
903 match self.components.len() {
904 0 => (*self).clone(),
909 fn file_path(&self) -> WindowsPath {
914 components: match self.filename() {
916 Some(ref f) => ~[(*f).clone()],
921 fn push_rel(&self, other: &WindowsPath) -> WindowsPath {
922 assert!(!other.is_absolute);
923 self.push_many(other.components)
926 fn unsafe_join(&self, other: &WindowsPath) -> WindowsPath {
927 /* rhs not absolute is simple push */
928 if !other.is_absolute {
929 return self.push_many(other.components);
932 /* if rhs has a host set, then the whole thing wins */
936 host: Some((*host).clone()),
937 device: other.device.clone(),
939 components: other.components.clone(),
945 /* if rhs has a device set, then a part wins */
947 Some(ref device) => {
950 device: Some((*device).clone()),
952 components: other.components.clone(),
958 /* fallback: host and device of lhs win, but the
959 whole path of the right */
961 host: self.host.clone(),
962 device: self.device.clone(),
963 is_absolute: self.is_absolute || other.is_absolute,
964 components: other.components.clone(),
968 fn is_restricted(&self) -> bool {
969 match self.filestem() {
971 // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use
972 // to_ascii_move and to_str_move to not do a unnecessary copy.
973 match stem.to_ascii().to_lower().to_str_ascii() {
974 ~"con" | ~"aux" | ~"com1" | ~"com2" | ~"com3" | ~"com4" |
975 ~"lpt1" | ~"lpt2" | ~"lpt3" | ~"prn" | ~"nul" => true,
983 fn push_many<S: Str>(&self, cs: &[S]) -> WindowsPath {
984 let mut v = self.components.clone();
986 for s in e.as_slice().split_iter(windows::is_sep) {
992 // tedious, but as-is, we can't use ..self
994 host: self.host.clone(),
995 device: self.device.clone(),
996 is_absolute: self.is_absolute,
1001 fn push(&self, s: &str) -> WindowsPath {
1002 let mut v = self.components.clone();
1003 for s in s.split_iter(windows::is_sep) {
1005 v.push(s.to_owned())
1008 WindowsPath { components: v, ..(*self).clone() }
1011 fn pop(&self) -> WindowsPath {
1012 let mut cs = self.components.clone();
1017 host: self.host.clone(),
1018 device: self.device.clone(),
1019 is_absolute: self.is_absolute,
1024 fn normalize(&self) -> WindowsPath {
1026 host: self.host.clone(),
1027 device: match self.device {
1030 // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use
1031 // to_ascii_move and to_str_move to not do a unnecessary copy.
1032 Some(ref device) => Some(device.to_ascii().to_upper().to_str_ascii())
1034 is_absolute: self.is_absolute,
1035 components: normalize(self.components)
1039 fn is_absolute(&self) -> bool {
1043 fn is_ancestor_of(&self, other: &WindowsPath) -> bool {
1045 (!other.components.is_empty() && !(self.components.is_empty() && !self.is_absolute) &&
1046 self.is_ancestor_of(&other.pop()))
1049 fn components(self) -> ~[~str] { self.components }
1052 pub fn normalize(components: &[~str]) -> ~[~str] {
1054 for c in components.iter() {
1055 if *c == ~"." && components.len() > 1 { loop; }
1056 if *c == ~"" { loop; }
1057 if *c == ~".." && cs.len() != 0 {
1061 cs.push((*c).clone());
1066 // Various posix helpers.
1070 pub fn is_sep(u: char) -> bool {
1076 // Various windows helpers.
1079 use option::{None, Option, Some};
1082 pub fn is_sep(u: char) -> bool {
1083 u == '/' || u == '\\'
1086 pub fn extract_unc_prefix(s: &str) -> Option<(~str,~str)> {
1088 (s[0] == '\\' as u8 || s[0] == '/' as u8) &&
1092 if is_sep(s[i] as char) {
1093 let pre = s.slice(2, i).to_owned();
1094 let rest = s.slice(i, s.len()).to_owned();
1095 return Some((pre, rest));
1103 pub fn extract_drive_prefix(s: &str) -> Option<(~str,~str)> {
1106 libc::isalpha(s[0] as libc::c_int) != 0 &&
1107 s[1] == ':' as u8) {
1108 let rest = if s.len() == 2 {
1111 s.slice(2, s.len()).to_owned()
1113 return Some((s.slice(0,1).to_owned(), rest));
1122 use option::{None, Some};
1123 use path::{PosixPath, WindowsPath, windows};
1126 fn test_double_slash_collapsing() {
1127 let path = PosixPath("tmp/");
1128 let path = path.push("/hmm");
1129 let path = path.normalize();
1130 assert_eq!(~"tmp/hmm", path.to_str());
1132 let path = WindowsPath("tmp/");
1133 let path = path.push("/hmm");
1134 let path = path.normalize();
1135 assert_eq!(~"tmp\\hmm", path.to_str());
1139 fn test_filetype_foo_bar() {
1140 let wp = PosixPath("foo.bar");
1141 assert_eq!(wp.filetype(), Some(~".bar"));
1143 let wp = WindowsPath("foo.bar");
1144 assert_eq!(wp.filetype(), Some(~".bar"));
1148 fn test_filetype_foo() {
1149 let wp = PosixPath("foo");
1150 assert_eq!(wp.filetype(), None);
1152 let wp = WindowsPath("foo");
1153 assert_eq!(wp.filetype(), None);
1157 fn test_posix_paths() {
1158 fn t(wp: &PosixPath, s: &str) {
1159 let ss = wp.to_str();
1160 let sss = s.to_owned();
1162 debug!("got %s", ss);
1163 debug!("expected %s", sss);
1164 assert_eq!(ss, sss);
1168 t(&(PosixPath("hi")), "hi");
1169 t(&(PosixPath("/lib")), "/lib");
1170 t(&(PosixPath("hi/there")), "hi/there");
1171 t(&(PosixPath("hi/there.txt")), "hi/there.txt");
1173 t(&(PosixPath("hi/there.txt")), "hi/there.txt");
1174 t(&(PosixPath("hi/there.txt")
1175 .with_filetype("")), "hi/there");
1177 t(&(PosixPath("/a/b/c/there.txt")
1178 .with_dirname("hi")), "hi/there.txt");
1180 t(&(PosixPath("hi/there.txt")
1181 .with_dirname(".")), "./there.txt");
1183 t(&(PosixPath("a/b/c")
1184 .push("..")), "a/b/c/..");
1186 t(&(PosixPath("there.txt")
1187 .with_filetype("o")), "there.o");
1189 t(&(PosixPath("hi/there.txt")
1190 .with_filetype("o")), "hi/there.o");
1192 t(&(PosixPath("hi/there.txt")
1194 .with_dirname("/usr/lib")),
1195 "/usr/lib/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("/usr/bin/rust")
1208 .push_many([~"lib", ~"thingy.so"])
1209 .with_filestem("librustc")),
1210 "/usr/bin/rust/lib/librustc.so");
1215 fn test_posix_push_with_backslash() {
1216 let a = PosixPath("/aaa/bbb");
1217 let b = a.push("x\\y"); // \ is not a file separator for posix paths
1218 assert_eq!(a.components.len(), 2);
1219 assert_eq!(b.components.len(), 3);
1223 fn test_normalize() {
1224 fn t(wp: &PosixPath, s: &str) {
1225 let ss = wp.to_str();
1226 let sss = s.to_owned();
1228 debug!("got %s", ss);
1229 debug!("expected %s", sss);
1230 assert_eq!(ss, sss);
1234 t(&(PosixPath("hi/there.txt")
1235 .with_dirname(".").normalize()), "there.txt");
1237 t(&(PosixPath("a/b/../c/././/../foo.txt/").normalize()),
1240 t(&(PosixPath("a/b/c")
1241 .push("..").normalize()), "a/b");
1245 fn test_extract_unc_prefixes() {
1246 assert!(windows::extract_unc_prefix("\\\\").is_none());
1247 assert!(windows::extract_unc_prefix("//").is_none());
1248 assert!(windows::extract_unc_prefix("\\\\hi").is_none());
1249 assert!(windows::extract_unc_prefix("//hi").is_none());
1250 assert!(windows::extract_unc_prefix("\\\\hi\\") ==
1251 Some((~"hi", ~"\\")));
1252 assert!(windows::extract_unc_prefix("//hi\\") ==
1253 Some((~"hi", ~"\\")));
1254 assert!(windows::extract_unc_prefix("\\\\hi\\there") ==
1255 Some((~"hi", ~"\\there")));
1256 assert!(windows::extract_unc_prefix("//hi/there") ==
1257 Some((~"hi", ~"/there")));
1258 assert!(windows::extract_unc_prefix(
1259 "\\\\hi\\there\\friends.txt") ==
1260 Some((~"hi", ~"\\there\\friends.txt")));
1261 assert!(windows::extract_unc_prefix(
1262 "//hi\\there\\friends.txt") ==
1263 Some((~"hi", ~"\\there\\friends.txt")));
1267 fn test_extract_drive_prefixes() {
1268 assert!(windows::extract_drive_prefix("c").is_none());
1269 assert!(windows::extract_drive_prefix("c:") ==
1271 assert!(windows::extract_drive_prefix("d:") ==
1273 assert!(windows::extract_drive_prefix("z:") ==
1275 assert!(windows::extract_drive_prefix("c:\\hi") ==
1276 Some((~"c", ~"\\hi")));
1277 assert!(windows::extract_drive_prefix("d:hi") ==
1278 Some((~"d", ~"hi")));
1279 assert!(windows::extract_drive_prefix("c:hi\\there.txt") ==
1280 Some((~"c", ~"hi\\there.txt")));
1281 assert!(windows::extract_drive_prefix("c:\\hi\\there.txt") ==
1282 Some((~"c", ~"\\hi\\there.txt")));
1286 fn test_windows_paths() {
1287 fn t(wp: &WindowsPath, s: &str) {
1288 let ss = wp.to_str();
1289 let sss = s.to_owned();
1291 debug!("got %s", ss);
1292 debug!("expected %s", sss);
1293 assert_eq!(ss, sss);
1297 t(&(WindowsPath("hi")), "hi");
1298 t(&(WindowsPath("hi/there")), "hi\\there");
1299 t(&(WindowsPath("hi/there.txt")), "hi\\there.txt");
1301 t(&(WindowsPath("there.txt")
1302 .with_filetype("o")), "there.o");
1304 t(&(WindowsPath("hi/there.txt")
1305 .with_filetype("o")), "hi\\there.o");
1307 t(&(WindowsPath("hi/there.txt")
1309 .with_dirname("c:\\program files A")),
1310 "c:\\program files A\\there.o");
1312 t(&(WindowsPath("hi/there.txt")
1314 .with_dirname("c:\\program files B\\")),
1315 "c:\\program files B\\there.o");
1317 t(&(WindowsPath("hi/there.txt")
1319 .with_dirname("c:\\program files C\\/")),
1320 "c:\\program files C\\there.o");
1322 t(&(WindowsPath("c:\\program files (x86)\\rust")
1323 .push_many([~"lib", ~"thingy.dll"])
1324 .with_filename("librustc.dll")),
1325 "c:\\program files (x86)\\rust\\lib\\librustc.dll");
1327 t(&(WindowsPath("\\\\computer\\share")
1328 .unsafe_join(&WindowsPath("\\a"))),
1331 t(&(WindowsPath("//computer/share")
1332 .unsafe_join(&WindowsPath("\\a"))),
1335 t(&(WindowsPath("//computer/share")
1336 .unsafe_join(&WindowsPath("\\\\computer\\share"))),
1337 "\\\\computer\\share");
1339 t(&(WindowsPath("C:/whatever")
1340 .unsafe_join(&WindowsPath("//computer/share/a/b"))),
1341 "\\\\computer\\share\\a\\b");
1343 t(&(WindowsPath("C:")
1344 .unsafe_join(&WindowsPath("D:/foo"))),
1347 t(&(WindowsPath("C:")
1348 .unsafe_join(&WindowsPath("B"))),
1351 t(&(WindowsPath("C:")
1352 .unsafe_join(&WindowsPath("/foo"))),
1355 t(&(WindowsPath("C:\\")
1356 .unsafe_join(&WindowsPath("\\bar"))),
1360 .unsafe_join(&WindowsPath(""))),
1364 .unsafe_join(&WindowsPath("a"))),
1368 .unsafe_join(&WindowsPath("C:\\a"))),
1371 t(&(WindowsPath("c:\\foo")
1377 fn test_windows_path_restrictions() {
1378 assert_eq!(WindowsPath("hi").is_restricted(), false);
1379 assert_eq!(WindowsPath("C:\\NUL").is_restricted(), true);
1380 assert_eq!(WindowsPath("C:\\COM1.TXT").is_restricted(), true);
1381 assert_eq!(WindowsPath("c:\\prn.exe").is_restricted(), true);
1385 fn test_is_ancestor_of() {
1386 assert!(&PosixPath("/a/b").is_ancestor_of(&PosixPath("/a/b/c/d")));
1387 assert!(!&PosixPath("/a/b/c/d").is_ancestor_of(&PosixPath("/a/b")));
1388 assert!(!&PosixPath("/a/b").is_ancestor_of(&PosixPath("/c/d")));
1389 assert!(&PosixPath("/a/b").is_ancestor_of(&PosixPath("/a/b/c/d")));
1390 assert!(&PosixPath("/").is_ancestor_of(&PosixPath("/a/b/c")));
1391 assert!(!&PosixPath("/").is_ancestor_of(&PosixPath("")));
1392 assert!(!&PosixPath("/a/b/c").is_ancestor_of(&PosixPath("")));
1393 assert!(!&PosixPath("").is_ancestor_of(&PosixPath("/a/b/c")));
1395 assert!(&WindowsPath("C:\\a\\b").is_ancestor_of(&WindowsPath("C:\\a\\b\\c\\d")));
1396 assert!(!&WindowsPath("C:\\a\\b\\c\\d").is_ancestor_of(&WindowsPath("C:\\a\\b")));
1397 assert!(!&WindowsPath("C:\\a\\b").is_ancestor_of(&WindowsPath("C:\\c\\d")));
1398 assert!(&WindowsPath("C:\\a\\b").is_ancestor_of(&WindowsPath("C:\\a\\b\\c\\d")));
1399 assert!(&WindowsPath("C:\\").is_ancestor_of(&WindowsPath("C:\\a\\b\\c")));
1400 assert!(!&WindowsPath("C:\\").is_ancestor_of(&WindowsPath("")));
1401 assert!(!&WindowsPath("C:\\a\\b\\c").is_ancestor_of(&WindowsPath("")));
1402 assert!(!&WindowsPath("").is_ancestor_of(&WindowsPath("C:\\a\\b\\c")));
1407 fn test_relative_to1() {
1408 let p1 = PosixPath("/usr/bin/rustc");
1409 let p2 = PosixPath("/usr/lib/mylib");
1410 let res = p1.get_relative_to(&p2);
1411 assert_eq!(res, PosixPath("../lib"));
1413 let p1 = WindowsPath("C:\\usr\\bin\\rustc");
1414 let p2 = WindowsPath("C:\\usr\\lib\\mylib");
1415 let res = p1.get_relative_to(&p2);
1416 assert_eq!(res, WindowsPath("..\\lib"));
1421 fn test_relative_to2() {
1422 let p1 = PosixPath("/usr/bin/rustc");
1423 let p2 = PosixPath("/usr/bin/../lib/mylib");
1424 let res = p1.get_relative_to(&p2);
1425 assert_eq!(res, PosixPath("../lib"));
1427 let p1 = WindowsPath("C:\\usr\\bin\\rustc");
1428 let p2 = WindowsPath("C:\\usr\\bin\\..\\lib\\mylib");
1429 let res = p1.get_relative_to(&p2);
1430 assert_eq!(res, WindowsPath("..\\lib"));
1434 fn test_relative_to3() {
1435 let p1 = PosixPath("/usr/bin/whatever/rustc");
1436 let p2 = PosixPath("/usr/lib/whatever/mylib");
1437 let res = p1.get_relative_to(&p2);
1438 assert_eq!(res, PosixPath("../../lib/whatever"));
1440 let p1 = WindowsPath("C:\\usr\\bin\\whatever\\rustc");
1441 let p2 = WindowsPath("C:\\usr\\lib\\whatever\\mylib");
1442 let res = p1.get_relative_to(&p2);
1443 assert_eq!(res, WindowsPath("..\\..\\lib\\whatever"));
1448 fn test_relative_to4() {
1449 let p1 = PosixPath("/usr/bin/whatever/../rustc");
1450 let p2 = PosixPath("/usr/lib/whatever/mylib");
1451 let res = p1.get_relative_to(&p2);
1452 assert_eq!(res, PosixPath("../lib/whatever"));
1454 let p1 = WindowsPath("C:\\usr\\bin\\whatever\\..\\rustc");
1455 let p2 = WindowsPath("C:\\usr\\lib\\whatever\\mylib");
1456 let res = p1.get_relative_to(&p2);
1457 assert_eq!(res, WindowsPath("..\\lib\\whatever"));
1462 fn test_relative_to5() {
1463 let p1 = PosixPath("/usr/bin/whatever/../rustc");
1464 let p2 = PosixPath("/usr/lib/whatever/../mylib");
1465 let res = p1.get_relative_to(&p2);
1466 assert_eq!(res, PosixPath("../lib"));
1468 let p1 = WindowsPath("C:\\usr\\bin/whatever\\..\\rustc");
1469 let p2 = WindowsPath("C:\\usr\\lib\\whatever\\..\\mylib");
1470 let res = p1.get_relative_to(&p2);
1471 assert_eq!(res, WindowsPath("..\\lib"));
1475 fn test_relative_to6() {
1476 let p1 = PosixPath("/1");
1477 let p2 = PosixPath("/2/3");
1478 let res = p1.get_relative_to(&p2);
1479 assert_eq!(res, PosixPath("2"));
1481 let p1 = WindowsPath("C:\\1");
1482 let p2 = WindowsPath("C:\\2\\3");
1483 let res = p1.get_relative_to(&p2);
1484 assert_eq!(res, WindowsPath("2"));
1489 fn test_relative_to7() {
1490 let p1 = PosixPath("/1/2");
1491 let p2 = PosixPath("/3");
1492 let res = p1.get_relative_to(&p2);
1493 assert_eq!(res, PosixPath(".."));
1495 let p1 = WindowsPath("C:\\1\\2");
1496 let p2 = WindowsPath("C:\\3");
1497 let res = p1.get_relative_to(&p2);
1498 assert_eq!(res, WindowsPath(".."));
1503 fn test_relative_to8() {
1504 let p1 = PosixPath("/home/brian/Dev/rust/build/").push_rel(
1505 &PosixPath("stage2/lib/rustc/i686-unknown-linux-gnu/lib/librustc.so"));
1506 let p2 = PosixPath("/home/brian/Dev/rust/build/stage2/bin/..").push_rel(
1507 &PosixPath("lib/rustc/i686-unknown-linux-gnu/lib/libstd.so"));
1508 let res = p1.get_relative_to(&p2);
1509 debug!("test_relative_to8: %s vs. %s",
1511 PosixPath(".").to_str());
1512 assert_eq!(res, PosixPath("."));
1514 let p1 = WindowsPath("C:\\home\\brian\\Dev\\rust\\build\\").push_rel(
1515 &WindowsPath("stage2\\lib\\rustc\\i686-unknown-linux-gnu\\lib\\librustc.so"));
1516 let p2 = WindowsPath("\\home\\brian\\Dev\\rust\\build\\stage2\\bin\\..").push_rel(
1517 &WindowsPath("lib\\rustc\\i686-unknown-linux-gnu\\lib\\libstd.so"));
1518 let res = p1.get_relative_to(&p2);
1519 debug!("test_relative_to8: %s vs. %s",
1521 WindowsPath(".").to_str());
1522 assert_eq!(res, WindowsPath("."));