]> git.lizzy.rs Git - rust.git/blob - src/libstd/path.rs
Add externfn macro and correctly label fixed_stack_segments
[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         #[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) } {
388                 0 => Some(st),
389                 _ => None,
390             }
391         }
392     }
393
394     pub fn exists(&self) -> bool {
395         match self.stat() {
396             None => false,
397             Some(_) => true,
398         }
399     }
400
401     pub fn get_size(&self) -> Option<i64> {
402         match self.stat() {
403             None => None,
404             Some(ref st) => Some(st.st_size as i64),
405         }
406     }
407
408     pub fn get_mode(&self) -> Option<uint> {
409         match self.stat() {
410             None => None,
411             Some(ref st) => Some(st.st_mode as uint),
412         }
413     }
414 }
415
416 #[cfg(not(target_os = "win32"))]
417 impl PosixPath {
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) } {
423                 0 => Some(st),
424                 _ => None,
425             }
426         }
427     }
428
429     pub fn exists(&self) -> bool {
430         match self.stat() {
431             None => false,
432             Some(_) => true,
433         }
434     }
435
436     pub fn get_size(&self) -> Option<i64> {
437         match self.stat() {
438             None => None,
439             Some(ref st) => Some(st.st_size as i64),
440         }
441     }
442
443     pub fn get_mode(&self) -> Option<uint> {
444         match self.stat() {
445             None => None,
446             Some(ref st) => Some(st.st_mode as uint),
447         }
448     }
449
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() {
453             f(self);
454             self.pop().each_parent(f);
455         }
456     }
457
458 }
459
460 #[cfg(target_os = "freebsd")]
461 #[cfg(target_os = "linux")]
462 #[cfg(target_os = "macos")]
463 impl PosixPath {
464     pub fn get_atime(&self) -> Option<(i64, int)> {
465         match self.stat() {
466             None => None,
467             Some(ref st) => {
468                 Some((st.st_atime as i64,
469                       st.st_atime_nsec as int))
470             }
471         }
472     }
473
474     pub fn get_mtime(&self) -> Option<(i64, int)> {
475         match self.stat() {
476             None => None,
477             Some(ref st) => {
478                 Some((st.st_mtime as i64,
479                       st.st_mtime_nsec as int))
480             }
481         }
482     }
483
484     pub fn get_ctime(&self) -> Option<(i64, int)> {
485         match self.stat() {
486             None => None,
487             Some(ref st) => {
488                 Some((st.st_ctime as i64,
489                       st.st_ctime_nsec as int))
490             }
491         }
492     }
493 }
494
495 #[cfg(unix)]
496 impl PosixPath {
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) } {
502                 0 => Some(st),
503                 _ => None,
504             }
505         }
506     }
507 }
508
509 #[cfg(target_os = "freebsd")]
510 #[cfg(target_os = "macos")]
511 impl PosixPath {
512     pub fn get_birthtime(&self) -> Option<(i64, int)> {
513         match self.stat() {
514             None => None,
515             Some(ref st) => {
516                 Some((st.st_birthtime as i64,
517                       st.st_birthtime_nsec as int))
518             }
519         }
520     }
521 }
522
523 #[cfg(target_os = "win32")]
524 impl WindowsPath {
525     pub fn get_atime(&self) -> Option<(i64, int)> {
526         match self.stat() {
527             None => None,
528             Some(ref st) => {
529                 Some((st.st_atime as i64, 0))
530             }
531         }
532     }
533
534     pub fn get_mtime(&self) -> Option<(i64, int)> {
535         match self.stat() {
536             None => None,
537             Some(ref st) => {
538                 Some((st.st_mtime as i64, 0))
539             }
540         }
541     }
542
543     pub fn get_ctime(&self) -> Option<(i64, int)> {
544         match self.stat() {
545             None => None,
546             Some(ref st) => {
547                 Some((st.st_ctime as i64, 0))
548             }
549         }
550     }
551
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() {
555             f(self);
556             self.pop().each_parent(f);
557         }
558     }
559 }
560
561 impl ToStr for PosixPath {
562     fn to_str(&self) -> ~str {
563         let mut s = ~"";
564         if self.is_absolute {
565             s.push_str("/");
566         }
567         s + self.components.connect("/")
568     }
569 }
570
571 impl ToCStr for PosixPath {
572     fn to_c_str(&self) -> c_str::CString {
573         self.to_str().to_c_str()
574     }
575
576     unsafe fn to_c_str_unchecked(&self) -> c_str::CString {
577         self.to_str().to_c_str_unchecked()
578     }
579 }
580
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())})
587             .collect();
588         let is_absolute = (s.len() != 0 && s[0] == '/' as u8);
589         PosixPath {
590             is_absolute: is_absolute,
591             components: components,
592         }
593     }
594
595     fn dirname(&self) -> ~str {
596         let s = self.dir_path().to_str();
597         match s.len() {
598             0 => ~".",
599             _ => s,
600         }
601     }
602
603     fn filename(&self) -> Option<~str> {
604         match self.components.len() {
605             0 => None,
606             n => Some(self.components[n - 1].clone()),
607         }
608     }
609
610     fn filestem(&self) -> Option<~str> {
611         match self.filename() {
612             None => None,
613             Some(ref f) => {
614                 match f.rfind('.') {
615                     Some(p) => Some(f.slice_to(p).to_owned()),
616                     None => Some((*f).clone()),
617                 }
618             }
619         }
620     }
621
622     fn filetype(&self) -> Option<~str> {
623         match self.filename() {
624             None => None,
625             Some(ref f) => {
626                 match f.rfind('.') {
627                     Some(p) if p < f.len() => Some(f.slice_from(p).to_owned()),
628                     _ => None,
629                 }
630             }
631         }
632     }
633
634     fn with_dirname(&self, d: &str) -> PosixPath {
635         let dpath = PosixPath(d);
636         match self.filename() {
637             Some(ref f) => dpath.push(*f),
638             None => dpath,
639         }
640     }
641
642     fn with_filename(&self, f: &str) -> PosixPath {
643         assert!(!f.iter().all(posix::is_sep));
644         self.dir_path().push(f)
645     }
646
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),
651         }
652     }
653
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)),
660         }
661     }
662
663     fn dir_path(&self) -> PosixPath {
664         match self.components.len() {
665             0 => (*self).clone(),
666             _ => self.pop(),
667         }
668     }
669
670     fn file_path(&self) -> PosixPath {
671         let cs = match self.filename() {
672           None => ~[],
673           Some(ref f) => ~[(*f).clone()]
674         };
675         PosixPath {
676             is_absolute: false,
677             components: cs,
678         }
679     }
680
681     fn push_rel(&self, other: &PosixPath) -> PosixPath {
682         assert!(!other.is_absolute);
683         self.push_many(other.components)
684     }
685
686     fn unsafe_join(&self, other: &PosixPath) -> PosixPath {
687         if other.is_absolute {
688             PosixPath {
689                 is_absolute: true,
690                 components: other.components.clone(),
691             }
692         } else {
693             self.push_rel(other)
694         }
695     }
696
697     fn is_restricted(&self) -> bool {
698         false
699     }
700
701     fn push_many<S: Str>(&self, cs: &[S]) -> PosixPath {
702         let mut v = self.components.clone();
703         for e in cs.iter() {
704             for s in e.as_slice().split_iter(posix::is_sep) {
705                 if !s.is_empty() {
706                     v.push(s.to_owned())
707                 }
708             }
709         }
710         PosixPath {
711             is_absolute: self.is_absolute,
712             components: v,
713         }
714     }
715
716     fn push(&self, s: &str) -> PosixPath {
717         let mut v = self.components.clone();
718         for s in s.split_iter(posix::is_sep) {
719             if !s.is_empty() {
720                 v.push(s.to_owned())
721             }
722         }
723         PosixPath {
724             components: v,
725             ..(*self).clone()
726         }
727     }
728
729     fn pop(&self) -> PosixPath {
730         let mut cs = self.components.clone();
731         if cs.len() != 0 {
732             cs.pop();
733         }
734         PosixPath {
735             is_absolute: self.is_absolute,
736             components: cs,
737         } //..self }
738     }
739
740     fn normalize(&self) -> PosixPath {
741         PosixPath {
742             is_absolute: self.is_absolute,
743             components: normalize(self.components),
744         } // ..self }
745     }
746
747     fn is_absolute(&self) -> bool {
748         self.is_absolute
749     }
750
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());
754         self == other ||
755             (!other.components.is_empty() && !(self.components.is_empty() && !self.is_absolute) &&
756              self.is_ancestor_of(&other.pop()))
757     }
758
759    fn components(self) -> ~[~str] { self.components }
760 }
761
762
763 impl ToStr for WindowsPath {
764     fn to_str(&self) -> ~str {
765         let mut s = ~"";
766         match self.host {
767           Some(ref h) => {
768             s.push_str("\\\\");
769             s.push_str(*h);
770           }
771           None => { }
772         }
773         match self.device {
774           Some(ref d) => {
775             s.push_str(*d);
776             s.push_str(":");
777           }
778           None => { }
779         }
780         if self.is_absolute {
781             s.push_str("\\");
782         }
783         s + self.components.connect("\\")
784     }
785 }
786
787 impl c_str::ToCStr for WindowsPath {
788     fn to_c_str(&self) -> c_str::CString {
789         self.to_str().to_c_str()
790     }
791
792     unsafe fn to_c_str_unchecked(&self) -> c_str::CString {
793         self.to_str().to_c_str_unchecked()
794     }
795 }
796
797 impl GenericPath for WindowsPath {
798     fn from_str(s: &str) -> WindowsPath {
799         let host;
800         let device;
801         let rest;
802
803         match (
804             windows::extract_drive_prefix(s),
805             windows::extract_unc_prefix(s),
806         ) {
807             (Some((ref d, ref r)), _) => {
808                 host = None;
809                 device = Some((*d).clone());
810                 rest = (*r).clone();
811             }
812             (None, Some((ref h, ref r))) => {
813                 host = Some((*h).clone());
814                 device = None;
815                 rest = (*r).clone();
816             }
817             (None, None) => {
818                 host = None;
819                 device = None;
820                 rest = s.to_owned();
821             }
822         }
823
824         let components = rest.split_iter(windows::is_sep)
825             .filter_map(|s| if s.is_empty() {None} else {Some(s.to_owned())})
826             .collect();
827
828         let is_absolute = (rest.len() != 0 && windows::is_sep(rest[0] as char));
829         WindowsPath {
830             host: host,
831             device: device,
832             is_absolute: is_absolute,
833             components: components,
834         }
835     }
836
837     fn dirname(&self) -> ~str {
838         let s = self.dir_path().to_str();
839         match s.len() {
840             0 => ~".",
841             _ => s,
842         }
843     }
844
845     fn filename(&self) -> Option<~str> {
846         match self.components.len() {
847             0 => None,
848             n => Some(self.components[n - 1].clone()),
849         }
850     }
851
852     fn filestem(&self) -> Option<~str> {
853         match self.filename() {
854             None => None,
855             Some(ref f) => {
856                 match f.rfind('.') {
857                     Some(p) => Some(f.slice_to(p).to_owned()),
858                     None => Some((*f).clone()),
859                 }
860             }
861         }
862     }
863
864     fn filetype(&self) -> Option<~str> {
865         match self.filename() {
866           None => None,
867           Some(ref f) => {
868             match f.rfind('.') {
869                 Some(p) if p < f.len() => Some(f.slice_from(p).to_owned()),
870                 _ => None,
871             }
872           }
873         }
874     }
875
876     fn with_dirname(&self, d: &str) -> WindowsPath {
877         let dpath = WindowsPath(d);
878         match self.filename() {
879             Some(ref f) => dpath.push(*f),
880             None => dpath,
881         }
882     }
883
884     fn with_filename(&self, f: &str) -> WindowsPath {
885         assert!(! f.iter().all(windows::is_sep));
886         self.dir_path().push(f)
887     }
888
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),
893         }
894     }
895
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)),
902         }
903     }
904
905     fn dir_path(&self) -> WindowsPath {
906         match self.components.len() {
907             0 => (*self).clone(),
908             _ => self.pop(),
909         }
910     }
911
912     fn file_path(&self) -> WindowsPath {
913         WindowsPath {
914             host: None,
915             device: None,
916             is_absolute: false,
917             components: match self.filename() {
918                 None => ~[],
919                 Some(ref f) => ~[(*f).clone()],
920             }
921         }
922     }
923
924     fn push_rel(&self, other: &WindowsPath) -> WindowsPath {
925         assert!(!other.is_absolute);
926         self.push_many(other.components)
927     }
928
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);
933         }
934
935         /* if rhs has a host set, then the whole thing wins */
936         match other.host {
937             Some(ref host) => {
938                 return WindowsPath {
939                     host: Some((*host).clone()),
940                     device: other.device.clone(),
941                     is_absolute: true,
942                     components: other.components.clone(),
943                 };
944             }
945             _ => {}
946         }
947
948         /* if rhs has a device set, then a part wins */
949         match other.device {
950             Some(ref device) => {
951                 return WindowsPath {
952                     host: None,
953                     device: Some((*device).clone()),
954                     is_absolute: true,
955                     components: other.components.clone(),
956                 };
957             }
958             _ => {}
959         }
960
961         /* fallback: host and device of lhs win, but the
962            whole path of the right */
963         WindowsPath {
964             host: self.host.clone(),
965             device: self.device.clone(),
966             is_absolute: self.is_absolute || other.is_absolute,
967             components: other.components.clone(),
968         }
969     }
970
971     fn is_restricted(&self) -> bool {
972         match self.filestem() {
973             Some(stem) => {
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,
979                     _ => false
980                 }
981             },
982             None => false
983         }
984     }
985
986     fn push_many<S: Str>(&self, cs: &[S]) -> WindowsPath {
987         let mut v = self.components.clone();
988         for e in cs.iter() {
989             for s in e.as_slice().split_iter(windows::is_sep) {
990                 if !s.is_empty() {
991                     v.push(s.to_owned())
992                 }
993             }
994         }
995         // tedious, but as-is, we can't use ..self
996         WindowsPath {
997             host: self.host.clone(),
998             device: self.device.clone(),
999             is_absolute: self.is_absolute,
1000             components: v
1001         }
1002     }
1003
1004     fn push(&self, s: &str) -> WindowsPath {
1005         let mut v = self.components.clone();
1006         for s in s.split_iter(windows::is_sep) {
1007             if !s.is_empty() {
1008                 v.push(s.to_owned())
1009             }
1010         }
1011         WindowsPath { components: v, ..(*self).clone() }
1012     }
1013
1014     fn pop(&self) -> WindowsPath {
1015         let mut cs = self.components.clone();
1016         if cs.len() != 0 {
1017             cs.pop();
1018         }
1019         WindowsPath {
1020             host: self.host.clone(),
1021             device: self.device.clone(),
1022             is_absolute: self.is_absolute,
1023             components: cs,
1024         }
1025     }
1026
1027     fn normalize(&self) -> WindowsPath {
1028         WindowsPath {
1029             host: self.host.clone(),
1030             device: match self.device {
1031                 None => None,
1032
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())
1036             },
1037             is_absolute: self.is_absolute,
1038             components: normalize(self.components)
1039         }
1040     }
1041
1042     fn is_absolute(&self) -> bool {
1043         self.is_absolute
1044     }
1045
1046     fn is_ancestor_of(&self, other: &WindowsPath) -> bool {
1047         self == other ||
1048             (!other.components.is_empty() && !(self.components.is_empty() && !self.is_absolute) &&
1049              self.is_ancestor_of(&other.pop()))
1050     }
1051
1052    fn components(self) -> ~[~str] { self.components }
1053 }
1054
1055 pub fn normalize(components: &[~str]) -> ~[~str] {
1056     let mut cs = ~[];
1057     for c in components.iter() {
1058         if *c == ~"." && components.len() > 1 { loop; }
1059         if *c == ~"" { loop; }
1060         if *c == ~".." && cs.len() != 0 {
1061             cs.pop();
1062             loop;
1063         }
1064         cs.push((*c).clone());
1065     }
1066     cs
1067 }
1068
1069 // Various posix helpers.
1070 pub mod posix {
1071
1072     #[inline]
1073     pub fn is_sep(u: char) -> bool {
1074         u == '/'
1075     }
1076
1077 }
1078
1079 // Various windows helpers.
1080 pub mod windows {
1081     use libc;
1082     use option::{None, Option, Some};
1083
1084     #[inline]
1085     pub fn is_sep(u: char) -> bool {
1086         u == '/' || u == '\\'
1087     }
1088
1089     pub fn extract_unc_prefix(s: &str) -> Option<(~str,~str)> {
1090         if (s.len() > 1 &&
1091             (s[0] == '\\' as u8 || s[0] == '/' as u8) &&
1092             s[0] == s[1]) {
1093             let mut i = 2;
1094             while i < s.len() {
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));
1099                 }
1100                 i += 1;
1101             }
1102         }
1103         None
1104     }
1105
1106     pub fn extract_drive_prefix(s: &str) -> Option<(~str,~str)> {
1107         #[fixed_stack_segment]; #[inline(never)];
1108
1109         unsafe {
1110             if (s.len() > 1 &&
1111                 libc::isalpha(s[0] as libc::c_int) != 0 &&
1112                 s[1] == ':' as u8) {
1113                 let rest = if s.len() == 2 {
1114                     ~""
1115                 } else {
1116                     s.slice(2, s.len()).to_owned()
1117                 };
1118                 return Some((s.slice(0,1).to_owned(), rest));
1119             }
1120             None
1121         }
1122     }
1123 }
1124
1125 #[cfg(test)]
1126 mod tests {
1127     use option::{None, Some};
1128     use path::{PosixPath, WindowsPath, windows};
1129
1130     #[test]
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());
1136
1137         let path = WindowsPath("tmp/");
1138         let path = path.push("/hmm");
1139         let path = path.normalize();
1140         assert_eq!(~"tmp\\hmm", path.to_str());
1141     }
1142
1143     #[test]
1144     fn test_filetype_foo_bar() {
1145         let wp = PosixPath("foo.bar");
1146         assert_eq!(wp.filetype(), Some(~".bar"));
1147
1148         let wp = WindowsPath("foo.bar");
1149         assert_eq!(wp.filetype(), Some(~".bar"));
1150     }
1151
1152     #[test]
1153     fn test_filetype_foo() {
1154         let wp = PosixPath("foo");
1155         assert_eq!(wp.filetype(), None);
1156
1157         let wp = WindowsPath("foo");
1158         assert_eq!(wp.filetype(), None);
1159     }
1160
1161     #[test]
1162     fn test_posix_paths() {
1163         fn t(wp: &PosixPath, s: &str) {
1164             let ss = wp.to_str();
1165             let sss = s.to_owned();
1166             if (ss != sss) {
1167                 debug!("got %s", ss);
1168                 debug!("expected %s", sss);
1169                 assert_eq!(ss, sss);
1170             }
1171         }
1172
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");
1177
1178         t(&(PosixPath("hi/there.txt")), "hi/there.txt");
1179         t(&(PosixPath("hi/there.txt")
1180            .with_filetype("")), "hi/there");
1181
1182         t(&(PosixPath("/a/b/c/there.txt")
1183             .with_dirname("hi")), "hi/there.txt");
1184
1185         t(&(PosixPath("hi/there.txt")
1186             .with_dirname(".")), "./there.txt");
1187
1188         t(&(PosixPath("a/b/c")
1189             .push("..")), "a/b/c/..");
1190
1191         t(&(PosixPath("there.txt")
1192             .with_filetype("o")), "there.o");
1193
1194         t(&(PosixPath("hi/there.txt")
1195             .with_filetype("o")), "hi/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("hi/there.txt")
1208             .with_filetype("o")
1209             .with_dirname("/usr//lib//")),
1210             "/usr/lib/there.o");
1211
1212         t(&(PosixPath("/usr/bin/rust")
1213             .push_many([~"lib", ~"thingy.so"])
1214             .with_filestem("librustc")),
1215           "/usr/bin/rust/lib/librustc.so");
1216
1217     }
1218
1219     #[test]
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);
1225     }
1226
1227     #[test]
1228     fn test_normalize() {
1229         fn t(wp: &PosixPath, s: &str) {
1230             let ss = wp.to_str();
1231             let sss = s.to_owned();
1232             if (ss != sss) {
1233                 debug!("got %s", ss);
1234                 debug!("expected %s", sss);
1235                 assert_eq!(ss, sss);
1236             }
1237         }
1238
1239         t(&(PosixPath("hi/there.txt")
1240             .with_dirname(".").normalize()), "there.txt");
1241
1242         t(&(PosixPath("a/b/../c/././/../foo.txt/").normalize()),
1243           "a/foo.txt");
1244
1245         t(&(PosixPath("a/b/c")
1246             .push("..").normalize()), "a/b");
1247     }
1248
1249     #[test]
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")));
1269     }
1270
1271     #[test]
1272     fn test_extract_drive_prefixes() {
1273         assert!(windows::extract_drive_prefix("c").is_none());
1274         assert!(windows::extract_drive_prefix("c:") ==
1275                      Some((~"c", ~"")));
1276         assert!(windows::extract_drive_prefix("d:") ==
1277                      Some((~"d", ~"")));
1278         assert!(windows::extract_drive_prefix("z:") ==
1279                      Some((~"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")));
1288     }
1289
1290     #[test]
1291     fn test_windows_paths() {
1292         fn t(wp: &WindowsPath, s: &str) {
1293             let ss = wp.to_str();
1294             let sss = s.to_owned();
1295             if (ss != sss) {
1296                 debug!("got %s", ss);
1297                 debug!("expected %s", sss);
1298                 assert_eq!(ss, sss);
1299             }
1300         }
1301
1302         t(&(WindowsPath("hi")), "hi");
1303         t(&(WindowsPath("hi/there")), "hi\\there");
1304         t(&(WindowsPath("hi/there.txt")), "hi\\there.txt");
1305
1306         t(&(WindowsPath("there.txt")
1307             .with_filetype("o")), "there.o");
1308
1309         t(&(WindowsPath("hi/there.txt")
1310             .with_filetype("o")), "hi\\there.o");
1311
1312         t(&(WindowsPath("hi/there.txt")
1313             .with_filetype("o")
1314             .with_dirname("c:\\program files A")),
1315           "c:\\program files A\\there.o");
1316
1317         t(&(WindowsPath("hi/there.txt")
1318             .with_filetype("o")
1319             .with_dirname("c:\\program files B\\")),
1320           "c:\\program files B\\there.o");
1321
1322         t(&(WindowsPath("hi/there.txt")
1323             .with_filetype("o")
1324             .with_dirname("c:\\program files C\\/")),
1325             "c:\\program files C\\there.o");
1326
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");
1331
1332         t(&(WindowsPath("\\\\computer\\share")
1333             .unsafe_join(&WindowsPath("\\a"))),
1334           "\\\\computer\\a");
1335
1336         t(&(WindowsPath("//computer/share")
1337             .unsafe_join(&WindowsPath("\\a"))),
1338           "\\\\computer\\a");
1339
1340         t(&(WindowsPath("//computer/share")
1341             .unsafe_join(&WindowsPath("\\\\computer\\share"))),
1342           "\\\\computer\\share");
1343
1344         t(&(WindowsPath("C:/whatever")
1345             .unsafe_join(&WindowsPath("//computer/share/a/b"))),
1346           "\\\\computer\\share\\a\\b");
1347
1348         t(&(WindowsPath("C:")
1349             .unsafe_join(&WindowsPath("D:/foo"))),
1350           "D:\\foo");
1351
1352         t(&(WindowsPath("C:")
1353             .unsafe_join(&WindowsPath("B"))),
1354           "C:B");
1355
1356         t(&(WindowsPath("C:")
1357             .unsafe_join(&WindowsPath("/foo"))),
1358           "C:\\foo");
1359
1360         t(&(WindowsPath("C:\\")
1361             .unsafe_join(&WindowsPath("\\bar"))),
1362           "C:\\bar");
1363
1364         t(&(WindowsPath("")
1365             .unsafe_join(&WindowsPath(""))),
1366           "");
1367
1368         t(&(WindowsPath("")
1369             .unsafe_join(&WindowsPath("a"))),
1370           "a");
1371
1372         t(&(WindowsPath("")
1373             .unsafe_join(&WindowsPath("C:\\a"))),
1374           "C:\\a");
1375
1376         t(&(WindowsPath("c:\\foo")
1377             .normalize()),
1378           "C:\\foo");
1379     }
1380
1381     #[test]
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);
1387     }
1388
1389     #[test]
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")));
1399
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")));
1408
1409     }
1410
1411     #[test]
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"));
1417
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"));
1422
1423     }
1424
1425     #[test]
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"));
1431
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"));
1436     }
1437
1438     #[test]
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"));
1444
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"));
1449
1450     }
1451
1452     #[test]
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"));
1458
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"));
1463
1464     }
1465
1466     #[test]
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"));
1472
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"));
1477     }
1478
1479     #[test]
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"));
1485
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"));
1490
1491     }
1492
1493     #[test]
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(".."));
1499
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(".."));
1504
1505     }
1506
1507     #[test]
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",
1515                res.to_str(),
1516                PosixPath(".").to_str());
1517         assert_eq!(res, PosixPath("."));
1518
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",
1525                res.to_str(),
1526                WindowsPath(".").to_str());
1527         assert_eq!(res, WindowsPath("."));
1528
1529     }
1530
1531 }