]> git.lizzy.rs Git - rust.git/blob - src/libstd/path.rs
de7658b5710b32cded88809f57693383a8896b5e
[rust.git] / src / libstd / path.rs
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.
4 //
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.
10
11 /*!
12
13 Cross-platform file path handling
14
15 */
16
17 #[allow(missing_doc)];
18
19 use c_str::ToCStr;
20 use c_str;
21 use clone::Clone;
22 use cmp::Eq;
23 use container::Container;
24 use iterator::{Iterator, range};
25 use libc;
26 use num;
27 use option::{None, Option, Some};
28 use str::{OwnedStr, Str, StrSlice, StrVector};
29 use to_str::ToStr;
30 use ascii::{AsciiCast, AsciiStr};
31 use vec::{OwnedVector, ImmutableVector, OwnedCopyableVector};
32
33 #[cfg(windows)]
34 pub use Path = self::WindowsPath;
35 #[cfg(unix)]
36 pub use Path = self::PosixPath;
37
38 #[deriving(Clone, Eq)]
39 pub struct WindowsPath {
40     host: Option<~str>,
41     device: Option<~str>,
42     is_absolute: bool,
43     components: ~[~str],
44 }
45
46 pub fn WindowsPath(s: &str) -> WindowsPath {
47     GenericPath::from_str(s)
48 }
49
50 #[deriving(Clone, Eq)]
51 pub struct PosixPath {
52     is_absolute: bool,
53     components: ~[~str],
54 }
55
56 pub fn PosixPath(s: &str) -> PosixPath {
57     GenericPath::from_str(s)
58 }
59
60 pub trait GenericPath {
61     /// Converts a string to a Path
62     fn from_str(&str) -> Self;
63
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>;
79
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;
92
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;
99
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;
111
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;
119
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
122     /// as well.
123     fn normalize(&self) -> Self;
124
125     /// Returns `true` if `self` is an absolute path.
126     fn is_absolute(&self) -> bool;
127
128     /// True if `self` is an ancestor of `other`. See `test_is_ancestor_of` for examples
129     fn is_ancestor_of(&self, (&Self)) -> bool;
130
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();
137
138         let split1: &[~str] = abs1.components();
139         let split2: &[~str] = abs2.components();
140         let len1 = split1.len();
141         let len2 = split2.len();
142         assert!(len1 > 0);
143         assert!(len2 > 0);
144
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] {
149             start_idx += 1;
150         }
151
152         let mut path: ~[~str] = ~[];
153         for _ in range(start_idx, len1 - 1) { path.push(~".."); };
154
155         path.push_all(split2.slice(start_idx, len2 - 1));
156
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);
162         };
163         result
164     }
165
166     fn components(self) -> ~[~str];
167 }
168
169 #[cfg(target_os = "linux")]
170 #[cfg(target_os = "android")]
171 mod stat {
172     #[cfg(target_arch = "x86")]
173     pub mod arch {
174         use libc;
175
176         pub fn default_stat() -> libc::stat {
177             libc::stat {
178                 st_dev: 0,
179                 __pad1: 0,
180                 st_ino: 0,
181                 st_mode: 0,
182                 st_nlink: 0,
183                 st_uid: 0,
184                 st_gid: 0,
185                 st_rdev: 0,
186                 __pad2: 0,
187                 st_size: 0,
188                 st_blksize: 0,
189                 st_blocks: 0,
190                 st_atime: 0,
191                 st_atime_nsec: 0,
192                 st_mtime: 0,
193                 st_mtime_nsec: 0,
194                 st_ctime: 0,
195                 st_ctime_nsec: 0,
196                 __unused4: 0,
197                 __unused5: 0,
198             }
199         }
200     }
201
202     #[cfg(target_arch = "arm")]
203     pub mod arch {
204         use libc;
205
206         pub fn default_stat() -> libc::stat {
207             libc::stat {
208                 st_dev: 0,
209                 __pad0: [0, ..4],
210                 __st_ino: 0,
211                 st_mode: 0,
212                 st_nlink: 0,
213                 st_uid: 0,
214                 st_gid: 0,
215                 st_rdev: 0,
216                 __pad3: [0, ..4],
217                 st_size: 0,
218                 st_blksize: 0,
219                 st_blocks: 0,
220                 st_atime: 0,
221                 st_atime_nsec: 0,
222                 st_mtime: 0,
223                 st_mtime_nsec: 0,
224                 st_ctime: 0,
225                 st_ctime_nsec: 0,
226                 st_ino: 0
227             }
228         }
229     }
230
231     #[cfg(target_arch = "mips")]
232     pub mod arch {
233         use libc;
234
235         pub fn default_stat() -> libc::stat {
236             libc::stat {
237                 st_dev: 0,
238                 st_pad1: [0, ..3],
239                 st_ino: 0,
240                 st_mode: 0,
241                 st_nlink: 0,
242                 st_uid: 0,
243                 st_gid: 0,
244                 st_rdev: 0,
245                 st_pad2: [0, ..2],
246                 st_size: 0,
247                 st_pad3: 0,
248                 st_atime: 0,
249                 st_atime_nsec: 0,
250                 st_mtime: 0,
251                 st_mtime_nsec: 0,
252                 st_ctime: 0,
253                 st_ctime_nsec: 0,
254                 st_blksize: 0,
255                 st_blocks: 0,
256                 st_pad5: [0, ..14],
257             }
258         }
259     }
260
261     #[cfg(target_arch = "x86_64")]
262     pub mod arch {
263         use libc;
264
265         pub fn default_stat() -> libc::stat {
266             libc::stat {
267                 st_dev: 0,
268                 st_ino: 0,
269                 st_nlink: 0,
270                 st_mode: 0,
271                 st_uid: 0,
272                 st_gid: 0,
273                 __pad0: 0,
274                 st_rdev: 0,
275                 st_size: 0,
276                 st_blksize: 0,
277                 st_blocks: 0,
278                 st_atime: 0,
279                 st_atime_nsec: 0,
280                 st_mtime: 0,
281                 st_mtime_nsec: 0,
282                 st_ctime: 0,
283                 st_ctime_nsec: 0,
284                 __unused: [0, 0, 0],
285             }
286         }
287     }
288 }
289
290 #[cfg(target_os = "freebsd")]
291 mod stat {
292     #[cfg(target_arch = "x86_64")]
293     pub mod arch {
294         use libc;
295
296         pub fn default_stat() -> libc::stat {
297             libc::stat {
298                 st_dev: 0,
299                 st_ino: 0,
300                 st_mode: 0,
301                 st_nlink: 0,
302                 st_uid: 0,
303                 st_gid: 0,
304                 st_rdev: 0,
305                 st_atime: 0,
306                 st_atime_nsec: 0,
307                 st_mtime: 0,
308                 st_mtime_nsec: 0,
309                 st_ctime: 0,
310                 st_ctime_nsec: 0,
311                 st_size: 0,
312                 st_blocks: 0,
313                 st_blksize: 0,
314                 st_flags: 0,
315                 st_gen: 0,
316                 st_lspare: 0,
317                 st_birthtime: 0,
318                 st_birthtime_nsec: 0,
319                 __unused: [0, 0],
320             }
321         }
322     }
323 }
324
325 #[cfg(target_os = "macos")]
326 mod stat {
327     pub mod arch {
328         use libc;
329
330         pub fn default_stat() -> libc::stat {
331             libc::stat {
332                 st_dev: 0,
333                 st_mode: 0,
334                 st_nlink: 0,
335                 st_ino: 0,
336                 st_uid: 0,
337                 st_gid: 0,
338                 st_rdev: 0,
339                 st_atime: 0,
340                 st_atime_nsec: 0,
341                 st_mtime: 0,
342                 st_mtime_nsec: 0,
343                 st_ctime: 0,
344                 st_ctime_nsec: 0,
345                 st_birthtime: 0,
346                 st_birthtime_nsec: 0,
347                 st_size: 0,
348                 st_blocks: 0,
349                 st_blksize: 0,
350                 st_flags: 0,
351                 st_gen: 0,
352                 st_lspare: 0,
353                 st_qspare: [0, 0],
354             }
355         }
356     }
357 }
358
359 #[cfg(target_os = "win32")]
360 mod stat {
361     pub mod arch {
362         use libc;
363         pub fn default_stat() -> libc::stat {
364             libc::stat {
365                 st_dev: 0,
366                 st_ino: 0,
367                 st_mode: 0,
368                 st_nlink: 0,
369                 st_uid: 0,
370                 st_gid: 0,
371                 st_rdev: 0,
372                 st_size: 0,
373                 st_atime: 0,
374                 st_mtime: 0,
375                 st_ctime: 0,
376             }
377         }
378     }
379 }
380
381 #[cfg(target_os = "win32")]
382 impl WindowsPath {
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) } {
387                 0 => Some(st),
388                 _ => None,
389             }
390         }
391     }
392
393     pub fn exists(&self) -> bool {
394         match self.stat() {
395             None => false,
396             Some(_) => true,
397         }
398     }
399
400     pub fn get_size(&self) -> Option<i64> {
401         match self.stat() {
402             None => None,
403             Some(ref st) => Some(st.st_size as i64),
404         }
405     }
406
407     pub fn get_mode(&self) -> Option<uint> {
408         match self.stat() {
409             None => None,
410             Some(ref st) => Some(st.st_mode as uint),
411         }
412     }
413 }
414
415 #[cfg(not(target_os = "win32"))]
416 impl PosixPath {
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) } {
421                 0 => Some(st),
422                 _ => None,
423             }
424         }
425     }
426
427     pub fn exists(&self) -> bool {
428         match self.stat() {
429             None => false,
430             Some(_) => true,
431         }
432     }
433
434     pub fn get_size(&self) -> Option<i64> {
435         match self.stat() {
436             None => None,
437             Some(ref st) => Some(st.st_size as i64),
438         }
439     }
440
441     pub fn get_mode(&self) -> Option<uint> {
442         match self.stat() {
443             None => None,
444             Some(ref st) => Some(st.st_mode as uint),
445         }
446     }
447
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() {
451             f(self);
452             self.pop().each_parent(f);
453         }
454     }
455
456 }
457
458 #[cfg(target_os = "freebsd")]
459 #[cfg(target_os = "linux")]
460 #[cfg(target_os = "macos")]
461 impl PosixPath {
462     pub fn get_atime(&self) -> Option<(i64, int)> {
463         match self.stat() {
464             None => None,
465             Some(ref st) => {
466                 Some((st.st_atime as i64,
467                       st.st_atime_nsec as int))
468             }
469         }
470     }
471
472     pub fn get_mtime(&self) -> Option<(i64, int)> {
473         match self.stat() {
474             None => None,
475             Some(ref st) => {
476                 Some((st.st_mtime as i64,
477                       st.st_mtime_nsec as int))
478             }
479         }
480     }
481
482     pub fn get_ctime(&self) -> Option<(i64, int)> {
483         match self.stat() {
484             None => None,
485             Some(ref st) => {
486                 Some((st.st_ctime as i64,
487                       st.st_ctime_nsec as int))
488             }
489         }
490     }
491 }
492
493 #[cfg(unix)]
494 impl PosixPath {
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) } {
499                 0 => Some(st),
500                 _ => None,
501             }
502         }
503     }
504 }
505
506 #[cfg(target_os = "freebsd")]
507 #[cfg(target_os = "macos")]
508 impl PosixPath {
509     pub fn get_birthtime(&self) -> Option<(i64, int)> {
510         match self.stat() {
511             None => None,
512             Some(ref st) => {
513                 Some((st.st_birthtime as i64,
514                       st.st_birthtime_nsec as int))
515             }
516         }
517     }
518 }
519
520 #[cfg(target_os = "win32")]
521 impl WindowsPath {
522     pub fn get_atime(&self) -> Option<(i64, int)> {
523         match self.stat() {
524             None => None,
525             Some(ref st) => {
526                 Some((st.st_atime as i64, 0))
527             }
528         }
529     }
530
531     pub fn get_mtime(&self) -> Option<(i64, int)> {
532         match self.stat() {
533             None => None,
534             Some(ref st) => {
535                 Some((st.st_mtime as i64, 0))
536             }
537         }
538     }
539
540     pub fn get_ctime(&self) -> Option<(i64, int)> {
541         match self.stat() {
542             None => None,
543             Some(ref st) => {
544                 Some((st.st_ctime as i64, 0))
545             }
546         }
547     }
548
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() {
552             f(self);
553             self.pop().each_parent(f);
554         }
555     }
556 }
557
558 impl ToStr for PosixPath {
559     fn to_str(&self) -> ~str {
560         let mut s = ~"";
561         if self.is_absolute {
562             s.push_str("/");
563         }
564         s + self.components.connect("/")
565     }
566 }
567
568 impl ToCStr for PosixPath {
569     fn to_c_str(&self) -> c_str::CString {
570         self.to_str().to_c_str()
571     }
572
573     unsafe fn to_c_str_unchecked(&self) -> c_str::CString {
574         self.to_str().to_c_str_unchecked()
575     }
576 }
577
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())})
584             .collect();
585         let is_absolute = (s.len() != 0 && s[0] == '/' as u8);
586         PosixPath {
587             is_absolute: is_absolute,
588             components: components,
589         }
590     }
591
592     fn dirname(&self) -> ~str {
593         let s = self.dir_path().to_str();
594         match s.len() {
595             0 => ~".",
596             _ => s,
597         }
598     }
599
600     fn filename(&self) -> Option<~str> {
601         match self.components.len() {
602             0 => None,
603             n => Some(self.components[n - 1].clone()),
604         }
605     }
606
607     fn filestem(&self) -> Option<~str> {
608         match self.filename() {
609             None => None,
610             Some(ref f) => {
611                 match f.rfind('.') {
612                     Some(p) => Some(f.slice_to(p).to_owned()),
613                     None => Some((*f).clone()),
614                 }
615             }
616         }
617     }
618
619     fn filetype(&self) -> Option<~str> {
620         match self.filename() {
621             None => None,
622             Some(ref f) => {
623                 match f.rfind('.') {
624                     Some(p) if p < f.len() => Some(f.slice_from(p).to_owned()),
625                     _ => None,
626                 }
627             }
628         }
629     }
630
631     fn with_dirname(&self, d: &str) -> PosixPath {
632         let dpath = PosixPath(d);
633         match self.filename() {
634             Some(ref f) => dpath.push(*f),
635             None => dpath,
636         }
637     }
638
639     fn with_filename(&self, f: &str) -> PosixPath {
640         assert!(!f.iter().all(posix::is_sep));
641         self.dir_path().push(f)
642     }
643
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),
648         }
649     }
650
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)),
657         }
658     }
659
660     fn dir_path(&self) -> PosixPath {
661         match self.components.len() {
662             0 => (*self).clone(),
663             _ => self.pop(),
664         }
665     }
666
667     fn file_path(&self) -> PosixPath {
668         let cs = match self.filename() {
669           None => ~[],
670           Some(ref f) => ~[(*f).clone()]
671         };
672         PosixPath {
673             is_absolute: false,
674             components: cs,
675         }
676     }
677
678     fn push_rel(&self, other: &PosixPath) -> PosixPath {
679         assert!(!other.is_absolute);
680         self.push_many(other.components)
681     }
682
683     fn unsafe_join(&self, other: &PosixPath) -> PosixPath {
684         if other.is_absolute {
685             PosixPath {
686                 is_absolute: true,
687                 components: other.components.clone(),
688             }
689         } else {
690             self.push_rel(other)
691         }
692     }
693
694     fn is_restricted(&self) -> bool {
695         false
696     }
697
698     fn push_many<S: Str>(&self, cs: &[S]) -> PosixPath {
699         let mut v = self.components.clone();
700         for e in cs.iter() {
701             for s in e.as_slice().split_iter(posix::is_sep) {
702                 if !s.is_empty() {
703                     v.push(s.to_owned())
704                 }
705             }
706         }
707         PosixPath {
708             is_absolute: self.is_absolute,
709             components: v,
710         }
711     }
712
713     fn push(&self, s: &str) -> PosixPath {
714         let mut v = self.components.clone();
715         for s in s.split_iter(posix::is_sep) {
716             if !s.is_empty() {
717                 v.push(s.to_owned())
718             }
719         }
720         PosixPath {
721             components: v,
722             ..(*self).clone()
723         }
724     }
725
726     fn pop(&self) -> PosixPath {
727         let mut cs = self.components.clone();
728         if cs.len() != 0 {
729             cs.pop();
730         }
731         PosixPath {
732             is_absolute: self.is_absolute,
733             components: cs,
734         } //..self }
735     }
736
737     fn normalize(&self) -> PosixPath {
738         PosixPath {
739             is_absolute: self.is_absolute,
740             components: normalize(self.components),
741         } // ..self }
742     }
743
744     fn is_absolute(&self) -> bool {
745         self.is_absolute
746     }
747
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());
751         self == other ||
752             (!other.components.is_empty() && !(self.components.is_empty() && !self.is_absolute) &&
753              self.is_ancestor_of(&other.pop()))
754     }
755
756    fn components(self) -> ~[~str] { self.components }
757 }
758
759
760 impl ToStr for WindowsPath {
761     fn to_str(&self) -> ~str {
762         let mut s = ~"";
763         match self.host {
764           Some(ref h) => {
765             s.push_str("\\\\");
766             s.push_str(*h);
767           }
768           None => { }
769         }
770         match self.device {
771           Some(ref d) => {
772             s.push_str(*d);
773             s.push_str(":");
774           }
775           None => { }
776         }
777         if self.is_absolute {
778             s.push_str("\\");
779         }
780         s + self.components.connect("\\")
781     }
782 }
783
784 impl c_str::ToCStr for WindowsPath {
785     fn to_c_str(&self) -> c_str::CString {
786         self.to_str().to_c_str()
787     }
788
789     unsafe fn to_c_str_unchecked(&self) -> c_str::CString {
790         self.to_str().to_c_str_unchecked()
791     }
792 }
793
794 impl GenericPath for WindowsPath {
795     fn from_str(s: &str) -> WindowsPath {
796         let host;
797         let device;
798         let rest;
799
800         match (
801             windows::extract_drive_prefix(s),
802             windows::extract_unc_prefix(s),
803         ) {
804             (Some((ref d, ref r)), _) => {
805                 host = None;
806                 device = Some((*d).clone());
807                 rest = (*r).clone();
808             }
809             (None, Some((ref h, ref r))) => {
810                 host = Some((*h).clone());
811                 device = None;
812                 rest = (*r).clone();
813             }
814             (None, None) => {
815                 host = None;
816                 device = None;
817                 rest = s.to_owned();
818             }
819         }
820
821         let components = rest.split_iter(windows::is_sep)
822             .filter_map(|s| if s.is_empty() {None} else {Some(s.to_owned())})
823             .collect();
824
825         let is_absolute = (rest.len() != 0 && windows::is_sep(rest[0] as char));
826         WindowsPath {
827             host: host,
828             device: device,
829             is_absolute: is_absolute,
830             components: components,
831         }
832     }
833
834     fn dirname(&self) -> ~str {
835         let s = self.dir_path().to_str();
836         match s.len() {
837             0 => ~".",
838             _ => s,
839         }
840     }
841
842     fn filename(&self) -> Option<~str> {
843         match self.components.len() {
844             0 => None,
845             n => Some(self.components[n - 1].clone()),
846         }
847     }
848
849     fn filestem(&self) -> Option<~str> {
850         match self.filename() {
851             None => None,
852             Some(ref f) => {
853                 match f.rfind('.') {
854                     Some(p) => Some(f.slice_to(p).to_owned()),
855                     None => Some((*f).clone()),
856                 }
857             }
858         }
859     }
860
861     fn filetype(&self) -> Option<~str> {
862         match self.filename() {
863           None => None,
864           Some(ref f) => {
865             match f.rfind('.') {
866                 Some(p) if p < f.len() => Some(f.slice_from(p).to_owned()),
867                 _ => None,
868             }
869           }
870         }
871     }
872
873     fn with_dirname(&self, d: &str) -> WindowsPath {
874         let dpath = WindowsPath(d);
875         match self.filename() {
876             Some(ref f) => dpath.push(*f),
877             None => dpath,
878         }
879     }
880
881     fn with_filename(&self, f: &str) -> WindowsPath {
882         assert!(! f.iter().all(windows::is_sep));
883         self.dir_path().push(f)
884     }
885
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),
890         }
891     }
892
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)),
899         }
900     }
901
902     fn dir_path(&self) -> WindowsPath {
903         match self.components.len() {
904             0 => (*self).clone(),
905             _ => self.pop(),
906         }
907     }
908
909     fn file_path(&self) -> WindowsPath {
910         WindowsPath {
911             host: None,
912             device: None,
913             is_absolute: false,
914             components: match self.filename() {
915                 None => ~[],
916                 Some(ref f) => ~[(*f).clone()],
917             }
918         }
919     }
920
921     fn push_rel(&self, other: &WindowsPath) -> WindowsPath {
922         assert!(!other.is_absolute);
923         self.push_many(other.components)
924     }
925
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);
930         }
931
932         /* if rhs has a host set, then the whole thing wins */
933         match other.host {
934             Some(ref host) => {
935                 return WindowsPath {
936                     host: Some((*host).clone()),
937                     device: other.device.clone(),
938                     is_absolute: true,
939                     components: other.components.clone(),
940                 };
941             }
942             _ => {}
943         }
944
945         /* if rhs has a device set, then a part wins */
946         match other.device {
947             Some(ref device) => {
948                 return WindowsPath {
949                     host: None,
950                     device: Some((*device).clone()),
951                     is_absolute: true,
952                     components: other.components.clone(),
953                 };
954             }
955             _ => {}
956         }
957
958         /* fallback: host and device of lhs win, but the
959            whole path of the right */
960         WindowsPath {
961             host: self.host.clone(),
962             device: self.device.clone(),
963             is_absolute: self.is_absolute || other.is_absolute,
964             components: other.components.clone(),
965         }
966     }
967
968     fn is_restricted(&self) -> bool {
969         match self.filestem() {
970             Some(stem) => {
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,
976                     _ => false
977                 }
978             },
979             None => false
980         }
981     }
982
983     fn push_many<S: Str>(&self, cs: &[S]) -> WindowsPath {
984         let mut v = self.components.clone();
985         for e in cs.iter() {
986             for s in e.as_slice().split_iter(windows::is_sep) {
987                 if !s.is_empty() {
988                     v.push(s.to_owned())
989                 }
990             }
991         }
992         // tedious, but as-is, we can't use ..self
993         WindowsPath {
994             host: self.host.clone(),
995             device: self.device.clone(),
996             is_absolute: self.is_absolute,
997             components: v
998         }
999     }
1000
1001     fn push(&self, s: &str) -> WindowsPath {
1002         let mut v = self.components.clone();
1003         for s in s.split_iter(windows::is_sep) {
1004             if !s.is_empty() {
1005                 v.push(s.to_owned())
1006             }
1007         }
1008         WindowsPath { components: v, ..(*self).clone() }
1009     }
1010
1011     fn pop(&self) -> WindowsPath {
1012         let mut cs = self.components.clone();
1013         if cs.len() != 0 {
1014             cs.pop();
1015         }
1016         WindowsPath {
1017             host: self.host.clone(),
1018             device: self.device.clone(),
1019             is_absolute: self.is_absolute,
1020             components: cs,
1021         }
1022     }
1023
1024     fn normalize(&self) -> WindowsPath {
1025         WindowsPath {
1026             host: self.host.clone(),
1027             device: match self.device {
1028                 None => None,
1029
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())
1033             },
1034             is_absolute: self.is_absolute,
1035             components: normalize(self.components)
1036         }
1037     }
1038
1039     fn is_absolute(&self) -> bool {
1040         self.is_absolute
1041     }
1042
1043     fn is_ancestor_of(&self, other: &WindowsPath) -> bool {
1044         self == other ||
1045             (!other.components.is_empty() && !(self.components.is_empty() && !self.is_absolute) &&
1046              self.is_ancestor_of(&other.pop()))
1047     }
1048
1049    fn components(self) -> ~[~str] { self.components }
1050 }
1051
1052 pub fn normalize(components: &[~str]) -> ~[~str] {
1053     let mut cs = ~[];
1054     for c in components.iter() {
1055         if *c == ~"." && components.len() > 1 { loop; }
1056         if *c == ~"" { loop; }
1057         if *c == ~".." && cs.len() != 0 {
1058             cs.pop();
1059             loop;
1060         }
1061         cs.push((*c).clone());
1062     }
1063     cs
1064 }
1065
1066 // Various posix helpers.
1067 pub mod posix {
1068
1069     #[inline]
1070     pub fn is_sep(u: char) -> bool {
1071         u == '/'
1072     }
1073
1074 }
1075
1076 // Various windows helpers.
1077 pub mod windows {
1078     use libc;
1079     use option::{None, Option, Some};
1080
1081     #[inline]
1082     pub fn is_sep(u: char) -> bool {
1083         u == '/' || u == '\\'
1084     }
1085
1086     pub fn extract_unc_prefix(s: &str) -> Option<(~str,~str)> {
1087         if (s.len() > 1 &&
1088             (s[0] == '\\' as u8 || s[0] == '/' as u8) &&
1089             s[0] == s[1]) {
1090             let mut i = 2;
1091             while i < s.len() {
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));
1096                 }
1097                 i += 1;
1098             }
1099         }
1100         None
1101     }
1102
1103     pub fn extract_drive_prefix(s: &str) -> Option<(~str,~str)> {
1104         unsafe {
1105             if (s.len() > 1 &&
1106                 libc::isalpha(s[0] as libc::c_int) != 0 &&
1107                 s[1] == ':' as u8) {
1108                 let rest = if s.len() == 2 {
1109                     ~""
1110                 } else {
1111                     s.slice(2, s.len()).to_owned()
1112                 };
1113                 return Some((s.slice(0,1).to_owned(), rest));
1114             }
1115             None
1116         }
1117     }
1118 }
1119
1120 #[cfg(test)]
1121 mod tests {
1122     use option::{None, Some};
1123     use path::{PosixPath, WindowsPath, windows};
1124
1125     #[test]
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());
1131
1132         let path = WindowsPath("tmp/");
1133         let path = path.push("/hmm");
1134         let path = path.normalize();
1135         assert_eq!(~"tmp\\hmm", path.to_str());
1136     }
1137
1138     #[test]
1139     fn test_filetype_foo_bar() {
1140         let wp = PosixPath("foo.bar");
1141         assert_eq!(wp.filetype(), Some(~".bar"));
1142
1143         let wp = WindowsPath("foo.bar");
1144         assert_eq!(wp.filetype(), Some(~".bar"));
1145     }
1146
1147     #[test]
1148     fn test_filetype_foo() {
1149         let wp = PosixPath("foo");
1150         assert_eq!(wp.filetype(), None);
1151
1152         let wp = WindowsPath("foo");
1153         assert_eq!(wp.filetype(), None);
1154     }
1155
1156     #[test]
1157     fn test_posix_paths() {
1158         fn t(wp: &PosixPath, s: &str) {
1159             let ss = wp.to_str();
1160             let sss = s.to_owned();
1161             if (ss != sss) {
1162                 debug!("got %s", ss);
1163                 debug!("expected %s", sss);
1164                 assert_eq!(ss, sss);
1165             }
1166         }
1167
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");
1172
1173         t(&(PosixPath("hi/there.txt")), "hi/there.txt");
1174         t(&(PosixPath("hi/there.txt")
1175            .with_filetype("")), "hi/there");
1176
1177         t(&(PosixPath("/a/b/c/there.txt")
1178             .with_dirname("hi")), "hi/there.txt");
1179
1180         t(&(PosixPath("hi/there.txt")
1181             .with_dirname(".")), "./there.txt");
1182
1183         t(&(PosixPath("a/b/c")
1184             .push("..")), "a/b/c/..");
1185
1186         t(&(PosixPath("there.txt")
1187             .with_filetype("o")), "there.o");
1188
1189         t(&(PosixPath("hi/there.txt")
1190             .with_filetype("o")), "hi/there.o");
1191
1192         t(&(PosixPath("hi/there.txt")
1193             .with_filetype("o")
1194             .with_dirname("/usr/lib")),
1195           "/usr/lib/there.o");
1196
1197         t(&(PosixPath("hi/there.txt")
1198             .with_filetype("o")
1199             .with_dirname("/usr/lib/")),
1200           "/usr/lib/there.o");
1201
1202         t(&(PosixPath("hi/there.txt")
1203             .with_filetype("o")
1204             .with_dirname("/usr//lib//")),
1205             "/usr/lib/there.o");
1206
1207         t(&(PosixPath("/usr/bin/rust")
1208             .push_many([~"lib", ~"thingy.so"])
1209             .with_filestem("librustc")),
1210           "/usr/bin/rust/lib/librustc.so");
1211
1212     }
1213
1214     #[test]
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);
1220     }
1221
1222     #[test]
1223     fn test_normalize() {
1224         fn t(wp: &PosixPath, s: &str) {
1225             let ss = wp.to_str();
1226             let sss = s.to_owned();
1227             if (ss != sss) {
1228                 debug!("got %s", ss);
1229                 debug!("expected %s", sss);
1230                 assert_eq!(ss, sss);
1231             }
1232         }
1233
1234         t(&(PosixPath("hi/there.txt")
1235             .with_dirname(".").normalize()), "there.txt");
1236
1237         t(&(PosixPath("a/b/../c/././/../foo.txt/").normalize()),
1238           "a/foo.txt");
1239
1240         t(&(PosixPath("a/b/c")
1241             .push("..").normalize()), "a/b");
1242     }
1243
1244     #[test]
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")));
1264     }
1265
1266     #[test]
1267     fn test_extract_drive_prefixes() {
1268         assert!(windows::extract_drive_prefix("c").is_none());
1269         assert!(windows::extract_drive_prefix("c:") ==
1270                      Some((~"c", ~"")));
1271         assert!(windows::extract_drive_prefix("d:") ==
1272                      Some((~"d", ~"")));
1273         assert!(windows::extract_drive_prefix("z:") ==
1274                      Some((~"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")));
1283     }
1284
1285     #[test]
1286     fn test_windows_paths() {
1287         fn t(wp: &WindowsPath, s: &str) {
1288             let ss = wp.to_str();
1289             let sss = s.to_owned();
1290             if (ss != sss) {
1291                 debug!("got %s", ss);
1292                 debug!("expected %s", sss);
1293                 assert_eq!(ss, sss);
1294             }
1295         }
1296
1297         t(&(WindowsPath("hi")), "hi");
1298         t(&(WindowsPath("hi/there")), "hi\\there");
1299         t(&(WindowsPath("hi/there.txt")), "hi\\there.txt");
1300
1301         t(&(WindowsPath("there.txt")
1302             .with_filetype("o")), "there.o");
1303
1304         t(&(WindowsPath("hi/there.txt")
1305             .with_filetype("o")), "hi\\there.o");
1306
1307         t(&(WindowsPath("hi/there.txt")
1308             .with_filetype("o")
1309             .with_dirname("c:\\program files A")),
1310           "c:\\program files A\\there.o");
1311
1312         t(&(WindowsPath("hi/there.txt")
1313             .with_filetype("o")
1314             .with_dirname("c:\\program files B\\")),
1315           "c:\\program files B\\there.o");
1316
1317         t(&(WindowsPath("hi/there.txt")
1318             .with_filetype("o")
1319             .with_dirname("c:\\program files C\\/")),
1320             "c:\\program files C\\there.o");
1321
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");
1326
1327         t(&(WindowsPath("\\\\computer\\share")
1328             .unsafe_join(&WindowsPath("\\a"))),
1329           "\\\\computer\\a");
1330
1331         t(&(WindowsPath("//computer/share")
1332             .unsafe_join(&WindowsPath("\\a"))),
1333           "\\\\computer\\a");
1334
1335         t(&(WindowsPath("//computer/share")
1336             .unsafe_join(&WindowsPath("\\\\computer\\share"))),
1337           "\\\\computer\\share");
1338
1339         t(&(WindowsPath("C:/whatever")
1340             .unsafe_join(&WindowsPath("//computer/share/a/b"))),
1341           "\\\\computer\\share\\a\\b");
1342
1343         t(&(WindowsPath("C:")
1344             .unsafe_join(&WindowsPath("D:/foo"))),
1345           "D:\\foo");
1346
1347         t(&(WindowsPath("C:")
1348             .unsafe_join(&WindowsPath("B"))),
1349           "C:B");
1350
1351         t(&(WindowsPath("C:")
1352             .unsafe_join(&WindowsPath("/foo"))),
1353           "C:\\foo");
1354
1355         t(&(WindowsPath("C:\\")
1356             .unsafe_join(&WindowsPath("\\bar"))),
1357           "C:\\bar");
1358
1359         t(&(WindowsPath("")
1360             .unsafe_join(&WindowsPath(""))),
1361           "");
1362
1363         t(&(WindowsPath("")
1364             .unsafe_join(&WindowsPath("a"))),
1365           "a");
1366
1367         t(&(WindowsPath("")
1368             .unsafe_join(&WindowsPath("C:\\a"))),
1369           "C:\\a");
1370
1371         t(&(WindowsPath("c:\\foo")
1372             .normalize()),
1373           "C:\\foo");
1374     }
1375
1376     #[test]
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);
1382     }
1383
1384     #[test]
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")));
1394
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")));
1403
1404     }
1405
1406     #[test]
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"));
1412
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"));
1417
1418     }
1419
1420     #[test]
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"));
1426
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"));
1431     }
1432
1433     #[test]
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"));
1439
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"));
1444
1445     }
1446
1447     #[test]
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"));
1453
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"));
1458
1459     }
1460
1461     #[test]
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"));
1467
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"));
1472     }
1473
1474     #[test]
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"));
1480
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"));
1485
1486     }
1487
1488     #[test]
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(".."));
1494
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(".."));
1499
1500     }
1501
1502     #[test]
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",
1510                res.to_str(),
1511                PosixPath(".").to_str());
1512         assert_eq!(res, PosixPath("."));
1513
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",
1520                res.to_str(),
1521                WindowsPath(".").to_str());
1522         assert_eq!(res, WindowsPath("."));
1523
1524     }
1525
1526 }