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)
|
this.alloc_os_str_as_wide_str(&os_str, memkind)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::get_first)]
|
|
||||||
fn convert_path<'a>(
|
fn convert_path<'a>(
|
||||||
&self,
|
&self,
|
||||||
os_str: Cow<'a, OsStr>,
|
os_str: Cow<'a, OsStr>,
|
||||||
@ -260,6 +259,65 @@ fn convert_path<'a>(
|
|||||||
let this = self.eval_context_ref();
|
let this = self.eval_context_ref();
|
||||||
let target_os = &this.tcx.sess.target.os;
|
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
|
// Below we assume that everything non-Windows works like Unix, at least
|
||||||
// when it comes to file system path conventions.
|
// when it comes to file system path conventions.
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
@ -268,102 +326,30 @@ fn convert_path<'a>(
|
|||||||
os_str
|
os_str
|
||||||
} else {
|
} else {
|
||||||
// Unix target, Windows host.
|
// Unix target, Windows host.
|
||||||
let (from, to) = match direction {
|
let mut path: Vec<u16> = os_str.encode_wide().collect();
|
||||||
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.
|
|
||||||
match direction {
|
match direction {
|
||||||
PathConversion::HostToTarget => {
|
PathConversion::HostToTarget => {
|
||||||
// If this is an absolute Windows path that starts with a drive letter (`C:/...`
|
windows_to_unix(&mut path);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
PathConversion::TargetToHost => {
|
PathConversion::TargetToHost => {
|
||||||
// If the path is `\C:\`, the leading backslash was probably added by the above code
|
unix_to_windows(&mut path);
|
||||||
// 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.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Cow::Owned(OsString::from_wide(&converted))
|
Cow::Owned(OsString::from_wide(&path))
|
||||||
};
|
};
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
return if target_os == "windows" {
|
return if target_os == "windows" {
|
||||||
// Windows target, Unix host.
|
// Windows target, Unix host.
|
||||||
let (from, to) = match direction {
|
let mut path: Vec<u8> = os_str.into_owned().into_encoded_bytes();
|
||||||
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.
|
|
||||||
match direction {
|
match direction {
|
||||||
PathConversion::HostToTarget => {
|
PathConversion::HostToTarget => {
|
||||||
// If the path is `/C:/`, the leading backslash was probably added by the below
|
unix_to_windows(&mut path);
|
||||||
// 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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
PathConversion::TargetToHost => {
|
PathConversion::TargetToHost => {
|
||||||
// If this starts with `//?/`, it was probably produced by the above code and we
|
windows_to_unix(&mut path);
|
||||||
// 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'/');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
Cow::Owned(OsString::from_vec(path))
|
||||||
Cow::Owned(OsString::from_vec(converted))
|
|
||||||
} else {
|
} else {
|
||||||
// Unix-on-Unix, all is fine.
|
// Unix-on-Unix, all is fine.
|
||||||
os_str
|
os_str
|
||||||
|
Loading…
Reference in New Issue
Block a user