about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSean Young <sean@mess.org>2021-09-26 14:21:22 +0100
committerSean Young <sean@mess.org>2021-10-01 19:54:57 +0100
commitfa4072f7d32221adf5edc8d57c9cb8fef3ddc96e (patch)
tree4a61de865dd7876b51f7233ee1f47758e0a8da5a
parentac8dd1b2f24dc62c962172b27433106b4e84dc62 (diff)
downloadrust-fa4072f7d32221adf5edc8d57c9cb8fef3ddc96e.tar.gz
rust-fa4072f7d32221adf5edc8d57c9cb8fef3ddc96e.zip
path.push() should work as expected on windows verbatim paths
-rw-r--r--library/std/src/path.rs53
-rw-r--r--library/std/src/path/tests.rs9
2 files changed, 55 insertions, 7 deletions
diff --git a/library/std/src/path.rs b/library/std/src/path.rs
index 9d5778ed48c..a45ecf6ea8c 100644
--- a/library/std/src/path.rs
+++ b/library/std/src/path.rs
@@ -1231,20 +1231,59 @@ impl PathBuf {
         let mut need_sep = self.as_mut_vec().last().map(|c| !is_sep_byte(*c)).unwrap_or(false);
 
         // in the special case of `C:` on Windows, do *not* add a separator
+        let comps = self.components();
+
+        if comps.prefix_len() > 0
+            && comps.prefix_len() == comps.path.len()
+            && comps.prefix.unwrap().is_drive()
         {
-            let comps = self.components();
-            if comps.prefix_len() > 0
-                && comps.prefix_len() == comps.path.len()
-                && comps.prefix.unwrap().is_drive()
-            {
-                need_sep = false
-            }
+            need_sep = false
         }
 
         // absolute `path` replaces `self`
         if path.is_absolute() || path.prefix().is_some() {
             self.as_mut_vec().truncate(0);
 
+        // verbatim paths need . and .. removed
+        } else if comps.prefix_verbatim() {
+            let mut buf: Vec<_> = comps.collect();
+            for c in path.components() {
+                match c {
+                    Component::RootDir => {
+                        buf.truncate(1);
+                        buf.push(c);
+                    }
+                    Component::CurDir => (),
+                    Component::ParentDir => {
+                        if let Some(Component::Normal(_)) = buf.last() {
+                            buf.pop();
+                        }
+                    }
+                    _ => buf.push(c),
+                }
+            }
+
+            let mut res = OsString::new();
+            let mut need_sep = false;
+
+            for c in buf {
+                if need_sep && c != Component::RootDir {
+                    res.push(MAIN_SEP_STR);
+                }
+                res.push(c.as_os_str());
+
+                need_sep = match c {
+                    Component::RootDir => false,
+                    Component::Prefix(prefix) => {
+                        !prefix.parsed.is_drive() && prefix.parsed.len() > 0
+                    }
+                    _ => true,
+                }
+            }
+
+            self.inner = res;
+            return;
+
         // `path` has a root but no prefix, e.g., `\windows` (Windows only)
         } else if path.has_root() {
             let prefix_len = self.components().prefix_remaining();
diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs
index ce23cf6cd21..3973a6829d3 100644
--- a/library/std/src/path/tests.rs
+++ b/library/std/src/path/tests.rs
@@ -1262,6 +1262,15 @@ pub fn test_push() {
         tp!("\\\\.\\foo", "..\\bar", "\\\\.\\foo\\..\\bar");
 
         tp!("\\\\?\\C:", "foo", "\\\\?\\C:\\foo"); // this is a weird one
+
+        tp!(r"\\?\C:\bar", "../foo", r"\\?\C:\foo");
+        tp!(r"\\?\C:\bar", "../../foo", r"\\?\C:\foo");
+        tp!(r"\\?\C:\", "../foo", r"\\?\C:\foo");
+        tp!(r"\\?\C:", r"D:\foo/./", r"D:\foo/./");
+        tp!(r"\\?\C:", r"\\?\D:\foo\.\", r"\\?\D:\foo\.\");
+        tp!(r"\\?\A:\x\y", "/foo", r"\\?\A:\foo");
+        tp!(r"\\?\A:", r"..\foo\.", r"\\?\A:\foo");
+        tp!(r"\\?\A:\x\y", r".\foo\.", r"\\?\A:\x\y\foo");
     }
 }