about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2024-04-19 09:17:37 +0200
committerRalf Jung <post@ralfj.de>2024-04-19 09:17:37 +0200
commitb9ed4cd70a3d8d4e540dae894589a5e37bc444fb (patch)
treefc947bcdcf0d366bd12da5b53104ef90ca428bf5 /src
parent39f0c4f8843d48168b3c31099111dffad433a818 (diff)
downloadrust-b9ed4cd70a3d8d4e540dae894589a5e37bc444fb.tar.gz
rust-b9ed4cd70a3d8d4e540dae894589a5e37bc444fb.zip
share code between win-to-unix and unix-to-win path conversion
Diffstat (limited to 'src')
-rw-r--r--src/tools/miri/src/shims/os_str.rs148
1 files changed, 67 insertions, 81 deletions
diff --git a/src/tools/miri/src/shims/os_str.rs b/src/tools/miri/src/shims/os_str.rs
index 925a35beb62..3e8c35d48ae 100644
--- a/src/tools/miri/src/shims/os_str.rs
+++ b/src/tools/miri/src/shims/os_str.rs
@@ -251,7 +251,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         this.alloc_os_str_as_wide_str(&os_str, memkind)
     }
 
-    #[allow(clippy::get_first)]
     fn convert_path<'a>(
         &self,
         os_str: Cow<'a, OsStr>,
@@ -260,6 +259,65 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         let this = self.eval_context_ref();
         let target_os = &this.tcx.sess.target.os;
 
+        /// Adjust a Windows path to Unix conventions such that it un-does everything that
+        /// `unix_to_windows` did, and such that if the Windows input path was absolute, then the
+        /// Unix output path is absolute.
+        fn windows_to_unix<T>(path: &mut Vec<T>)
+        where
+            T: From<u8> + Copy + Eq,
+        {
+            let sep = T::from(b'/');
+            // Make sure all path separators are `/`.
+            for c in path.iter_mut() {
+                if *c == b'\\'.into() {
+                    *c = sep;
+                }
+            }
+            // If this starts with `//?/`, it was probably produced by `unix_to_windows`` and we
+            // remove the `//?` that got added to get the Unix path back out.
+            if path.get(0..4) == Some(&[sep, sep, b'?'.into(), sep]) {
+                // Remove first 3 characters. It still starts with `/` so it is absolute on Unix.
+                path.splice(0..3, std::iter::empty());
+            }
+            // If it starts with a drive letter (`X:/`), convert it to an absolute Unix path.
+            else if path.get(1..3) == Some(&[b':'.into(), sep]) {
+                // We add a `/` at the beginning, to store the absolute Windows
+                // path in something that looks like an absolute Unix path.
+                path.insert(0, sep);
+            }
+        }
+
+        /// Adjust a Unix path to Windows conventions such that it un-does everything that
+        /// `windows_to_unix` did, and such that if the Unix input path was absolute, then the
+        /// Windows output path is absolute.
+        fn unix_to_windows<T>(path: &mut Vec<T>)
+        where
+            T: From<u8> + Copy + Eq,
+        {
+            let sep = T::from(b'\\');
+            // Make sure all path separators are `\`.
+            for c in path.iter_mut() {
+                if *c == b'/'.into() {
+                    *c = sep;
+                }
+            }
+            // If the path is `\X:\`, the leading separator was probably added by `windows_to_unix`
+            // and we should get rid of it again.
+            if path.get(2..4) == Some(&[b':'.into(), sep]) && path[0] == sep {
+                // The new path is still absolute on Windows.
+                path.remove(0);
+            }
+            // If this starts withs a `\` but not a `\\`, then this was absolute on Unix but is
+            // relative on Windows (relative to "the root of the current directory", e.g. the
+            // drive letter).
+            else if path.first() == Some(&sep) && path.get(1) != Some(&sep) {
+                // We add `\\?` so it starts with `\\?\` which is some magic path on Windows
+                // that *is* considered absolute. This way we store the absolute Unix path
+                // in something that looks like an absolute Windows path.
+                path.splice(0..0, [sep, sep, b'?'.into()]);
+            }
+        }
+
         // Below we assume that everything non-Windows works like Unix, at least
         // when it comes to file system path conventions.
         #[cfg(windows)]
@@ -268,102 +326,30 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             os_str
         } else {
             // Unix target, Windows host.
-            let (from, to) = match direction {
-                PathConversion::HostToTarget => ('\\', '/'),
-                PathConversion::TargetToHost => ('/', '\\'),
-            };
-            let mut converted = os_str
-                .encode_wide()
-                .map(|wchar| if wchar == from as u16 { to as u16 } else { wchar })
-                .collect::<Vec<_>>();
-            // We also have to ensure that absolute paths remain absolute.
+            let mut path: Vec<u16> = os_str.encode_wide().collect();
             match direction {
                 PathConversion::HostToTarget => {
-                    // If this is an absolute Windows path that starts with a drive letter (`C:/...`
-                    // after separator conversion), it would not be considered absolute by Unix
-                    // target code.
-                    if converted.get(1).copied() == Some(b':' as u16)
-                        && converted.get(2).copied() == Some(b'/' as u16)
-                    {
-                        // We add a `/` at the beginning, to store the absolute Windows
-                        // path in something that looks like an absolute Unix path.
-                        converted.insert(0, b'/' as u16);
-                    }
+                    windows_to_unix(&mut path);
                 }
                 PathConversion::TargetToHost => {
-                    // If the path is `\C:\`, the leading backslash was probably added by the above code
-                    // and we should get rid of it again.
-                    if converted.get(0).copied() == Some(b'\\' as u16)
-                        && converted.get(2).copied() == Some(b':' as u16)
-                        && converted.get(3).copied() == Some(b'\\' as u16)
-                    {
-                        converted.remove(0);
-                    }
-                    // If the path starts with `\\`, it is a magic Windows path. Conveniently, paths
-                    // starting with `//` on Unix are also magic where the first component can have
-                    // "application-specific" meaning, which is reflected e.g. by `path::absolute`
-                    // leaving leading `//` alone (but normalizing leading `///` to `/`). So we
-                    // don't have to do anything, the magic Windows path should work mostly fine as
-                    // a magic Unix path.
+                    unix_to_windows(&mut path);
                 }
             }
-            Cow::Owned(OsString::from_wide(&converted))
+            Cow::Owned(OsString::from_wide(&path))
         };
         #[cfg(unix)]
         return if target_os == "windows" {
             // Windows target, Unix host.
-            let (from, to) = match direction {
-                PathConversion::HostToTarget => (b'/', b'\\'),
-                PathConversion::TargetToHost => (b'\\', b'/'),
-            };
-            let mut converted = os_str
-                .as_bytes()
-                .iter()
-                .map(|&wchar| if wchar == from { to } else { wchar })
-                .collect::<Vec<_>>();
-            // We also have to ensure that absolute paths remain absolute.
+            let mut path: Vec<u8> = os_str.into_owned().into_encoded_bytes();
             match direction {
                 PathConversion::HostToTarget => {
-                    // If the path is `/C:/`, the leading backslash was probably added by the below
-                    // driver letter handling and we should get rid of it again.
-                    if converted.get(0).copied() == Some(b'\\')
-                        && converted.get(2).copied() == Some(b':')
-                        && converted.get(3).copied() == Some(b'\\')
-                    {
-                        converted.remove(0);
-                    }
-                    // If this starts withs a `\` but not a `\\`, then for Windows this is a
-                    // relative path (relative to "the root of the current directory", e.g. the
-                    // drive letter). But the host path on Unix is absolute as it starts with `/`.
-                    else if converted.get(0).copied() == Some(b'\\')
-                        && converted.get(1).copied() != Some(b'\\')
-                    {
-                        // We add `\\?` so it starts with `\\?\` which is some magic path on Windows
-                        // that *is* considered absolute. This way we store the absolute host path
-                        // in something that looks like an absolute path to the (Windows) target.
-                        converted.splice(0..0, b"\\\\?".iter().copied());
-                    }
+                    unix_to_windows(&mut path);
                 }
                 PathConversion::TargetToHost => {
-                    // If this starts with `//?/`, it was probably produced by the above code and we
-                    // remove the `//?` that got added to get the Unix path back out.
-                    if converted.get(0).copied() == Some(b'/')
-                        && converted.get(1).copied() == Some(b'/')
-                        && converted.get(2).copied() == Some(b'?')
-                        && converted.get(3).copied() == Some(b'/')
-                    {
-                        // Remove first 3 characters
-                        converted.splice(0..3, std::iter::empty());
-                    }
-                    // If it starts with a drive letter, convert it to an absolute Unix path.
-                    else if converted.get(1).copied() == Some(b':')
-                        && converted.get(2).copied() == Some(b'/')
-                    {
-                        converted.insert(0, b'/');
-                    }
+                    windows_to_unix(&mut path);
                 }
             }
-            Cow::Owned(OsString::from_vec(converted))
+            Cow::Owned(OsString::from_vec(path))
         } else {
             // Unix-on-Unix, all is fine.
             os_str