@@ -299,8 +299,8 @@ where
299299////////////////////////////////////////////////////////////////////////////////
300300
301301/// Says whether the first byte after the prefix is a separator.
302- fn has_physical_root ( s : & [ u8 ] , prefix : Option < Prefix < ' _ > > ) -> bool {
303- let path = if let Some ( p) = prefix { & s[ p. len ( ) ..] } else { s } ;
302+ fn has_physical_root ( s : & [ u8 ] , prefix : Option < Prefix < ' _ > > , pre_prefix_len : usize ) -> bool {
303+ let path = if let Some ( p) = prefix { & s[ p. len ( ) + pre_prefix_len ..] } else { s } ;
304304 !path. is_empty ( ) && is_sep_byte ( path[ 0 ] )
305305}
306306
@@ -600,6 +600,9 @@ pub struct Components<'a> {
600600 // The prefix as it was originally parsed, if any
601601 prefix : Option < Prefix < ' a > > ,
602602
603+ // The length of `./` in `//./` on Cygwin
604+ pre_prefix_len : usize ,
605+
603606 // true if path *physically* has a root separator; for most Windows
604607 // prefixes, it may have a "logical" root separator for the purposes of
605608 // normalization, e.g., \\server\share == \\server\share\.
@@ -643,7 +646,7 @@ impl<'a> Components<'a> {
643646 // how long is the prefix, if any?
644647 #[ inline]
645648 fn prefix_len ( & self ) -> usize {
646- self . prefix . as_ref ( ) . map ( Prefix :: len) . unwrap_or ( 0 )
649+ self . prefix . as_ref ( ) . map ( Prefix :: len) . unwrap_or ( 0 ) + self . pre_prefix_len
647650 }
648651
649652 #[ inline]
@@ -989,7 +992,14 @@ impl FusedIterator for Components<'_> {}
989992impl < ' a > PartialEq for Components < ' a > {
990993 #[ inline]
991994 fn eq ( & self , other : & Components < ' a > ) -> bool {
992- let Components { path : _, front : _, back : _, has_physical_root : _, prefix : _ } = self ;
995+ let Components {
996+ path : _,
997+ front : _,
998+ back : _,
999+ has_physical_root : _,
1000+ prefix : _,
1001+ pre_prefix_len : _,
1002+ } = self ;
9931003
9941004 // Fast path for exact matches, e.g. for hashmap lookups.
9951005 // Don't explicitly compare the prefix or has_physical_root fields since they'll
@@ -999,6 +1009,7 @@ impl<'a> PartialEq for Components<'a> {
9991009 && self . back == State :: Body
10001010 && other. back == State :: Body
10011011 && self . prefix_verbatim ( ) == other. prefix_verbatim ( )
1012+ && self . pre_prefix_len == other. pre_prefix_len
10021013 {
10031014 // possible future improvement: this could bail out earlier if there were a
10041015 // reverse memcmp/bcmp comparing back to front
@@ -1315,8 +1326,17 @@ impl PathBuf {
13151326 need_sep = false
13161327 }
13171328
1329+ let need_clear = if cfg ! ( target_os = "cygwin" ) {
1330+ // If path is absolute and its prefix is none, it is like `/foo`,
1331+ // and will be handled below.
1332+ path. prefix ( ) . is_some ( )
1333+ } else {
1334+ // On Unix: prefix is always None.
1335+ path. is_absolute ( ) || path. prefix ( ) . is_some ( )
1336+ } ;
1337+
13181338 // absolute `path` replaces `self`
1319- if path . is_absolute ( ) || path . prefix ( ) . is_some ( ) {
1339+ if need_clear {
13201340 self . inner . truncate ( 0 ) ;
13211341
13221342 // verbatim paths need . and .. removed
@@ -2862,11 +2882,15 @@ impl Path {
28622882 /// [`CurDir`]: Component::CurDir
28632883 #[ stable( feature = "rust1" , since = "1.0.0" ) ]
28642884 pub fn components ( & self ) -> Components < ' _ > {
2865- let prefix = parse_prefix ( self . as_os_str ( ) ) ;
2885+ let ( pre_prefix_len, prefix) = match parse_prefix ( self . as_os_str ( ) ) {
2886+ Some ( ( pre_prefix_len, prefix) ) => ( pre_prefix_len, Some ( prefix) ) ,
2887+ None => ( 0 , None ) ,
2888+ } ;
28662889 Components {
28672890 path : self . as_u8_slice ( ) ,
28682891 prefix,
2869- has_physical_root : has_physical_root ( self . as_u8_slice ( ) , prefix) ,
2892+ pre_prefix_len,
2893+ has_physical_root : has_physical_root ( self . as_u8_slice ( ) , prefix, pre_prefix_len) ,
28702894 front : State :: Prefix ,
28712895 back : State :: Body ,
28722896 }
@@ -3330,9 +3354,9 @@ impl Hash for Path {
33303354 fn hash < H : Hasher > ( & self , h : & mut H ) {
33313355 let bytes = self . as_u8_slice ( ) ;
33323356 let ( prefix_len, verbatim) = match parse_prefix ( & self . inner ) {
3333- Some ( prefix) => {
3357+ Some ( ( pre_prefix_len , prefix) ) => {
33343358 prefix. hash ( h) ;
3335- ( prefix. len ( ) , prefix. is_verbatim ( ) )
3359+ ( prefix. len ( ) + pre_prefix_len , prefix. is_verbatim ( ) )
33363360 }
33373361 None => ( 0 , false ) ,
33383362 } ;
@@ -3615,6 +3639,9 @@ impl Error for NormalizeError {}
36153639/// paths, this is currently equivalent to calling
36163640/// [`GetFullPathNameW`][windows-path].
36173641///
3642+ /// On Cygwin, this is currently equivalent to calling [`cygwin_conv_path`][cygwin-path]
3643+ /// with mode `CCP_WIN_A_TO_POSIX`.
3644+ ///
36183645/// Note that these [may change in the future][changes].
36193646///
36203647/// # Errors
@@ -3667,11 +3694,36 @@ impl Error for NormalizeError {}
36673694/// # fn main() {}
36683695/// ```
36693696///
3697+ /// ## Cygwin paths
3698+ ///
3699+ /// ```
3700+ /// # #[cfg(target_os = "cygwin")]
3701+ /// fn main() -> std::io::Result<()> {
3702+ /// use std::path::{self, Path};
3703+ ///
3704+ /// // Relative to absolute
3705+ /// let absolute = path::absolute("foo/./bar")?;
3706+ /// assert!(absolute.ends_with(r"foo/bar"));
3707+ ///
3708+ /// // Windows absolute to absolute
3709+ /// let absolute = path::absolute(r"C:\foo//test\..\./bar.rs")?;
3710+ /// assert!(absolute.ends_with("/c/foo/bar.rs"));
3711+ ///
3712+ /// // POSIX absolute to absolute
3713+ /// let absolute = path::absolute("/foo//test/.././bar.rs")?;
3714+ /// assert_eq!(absolute, Path::new("/foo//test/.././bar.rs"));
3715+ /// Ok(())
3716+ /// }
3717+ /// # #[cfg(not(target_os = "cygwin"))]
3718+ /// # fn main() {}
3719+ /// ```
3720+ ///
36703721/// Note that this [may change in the future][changes].
36713722///
36723723/// [changes]: io#platform-specific-behavior
36733724/// [posix-semantics]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13
36743725/// [windows-path]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfullpathnamew
3726+ /// [cygwin-path]: https://cygwin.com/cygwin-api/func-cygwin-conv-path.html
36753727#[ stable( feature = "absolute_path" , since = "1.79.0" ) ]
36763728pub fn absolute < P : AsRef < Path > > ( path : P ) -> io:: Result < PathBuf > {
36773729 let path = path. as_ref ( ) ;
0 commit comments