Auto merge of #3490 - RalfJung:paths, r=RalfJung
share code between win-to-unix and unix-to-win path conversion
This commit is contained in:
commit
134ee309c9
@ -251,7 +251,6 @@ fn alloc_path_as_wide_str(
|
||||
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 @@ fn convert_path<'a>(
|
||||
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 @@ fn convert_path<'a>(
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user