Skip to content

Commit 134ee30

Browse files
committed
Auto merge of rust-lang#3490 - RalfJung:paths, r=RalfJung
share code between win-to-unix and unix-to-win path conversion
2 parents 39f0c4f + b9ed4cd commit 134ee30

File tree

1 file changed

+67
-81
lines changed

1 file changed

+67
-81
lines changed

src/tools/miri/src/shims/os_str.rs

Lines changed: 67 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
251251
this.alloc_os_str_as_wide_str(&os_str, memkind)
252252
}
253253

254-
#[allow(clippy::get_first)]
255254
fn convert_path<'a>(
256255
&self,
257256
os_str: Cow<'a, OsStr>,
@@ -260,6 +259,65 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
260259
let this = self.eval_context_ref();
261260
let target_os = &this.tcx.sess.target.os;
262261

262+
/// Adjust a Windows path to Unix conventions such that it un-does everything that
263+
/// `unix_to_windows` did, and such that if the Windows input path was absolute, then the
264+
/// Unix output path is absolute.
265+
fn windows_to_unix<T>(path: &mut Vec<T>)
266+
where
267+
T: From<u8> + Copy + Eq,
268+
{
269+
let sep = T::from(b'/');
270+
// Make sure all path separators are `/`.
271+
for c in path.iter_mut() {
272+
if *c == b'\\'.into() {
273+
*c = sep;
274+
}
275+
}
276+
// If this starts with `//?/`, it was probably produced by `unix_to_windows`` and we
277+
// remove the `//?` that got added to get the Unix path back out.
278+
if path.get(0..4) == Some(&[sep, sep, b'?'.into(), sep]) {
279+
// Remove first 3 characters. It still starts with `/` so it is absolute on Unix.
280+
path.splice(0..3, std::iter::empty());
281+
}
282+
// If it starts with a drive letter (`X:/`), convert it to an absolute Unix path.
283+
else if path.get(1..3) == Some(&[b':'.into(), sep]) {
284+
// We add a `/` at the beginning, to store the absolute Windows
285+
// path in something that looks like an absolute Unix path.
286+
path.insert(0, sep);
287+
}
288+
}
289+
290+
/// Adjust a Unix path to Windows conventions such that it un-does everything that
291+
/// `windows_to_unix` did, and such that if the Unix input path was absolute, then the
292+
/// Windows output path is absolute.
293+
fn unix_to_windows<T>(path: &mut Vec<T>)
294+
where
295+
T: From<u8> + Copy + Eq,
296+
{
297+
let sep = T::from(b'\\');
298+
// Make sure all path separators are `\`.
299+
for c in path.iter_mut() {
300+
if *c == b'/'.into() {
301+
*c = sep;
302+
}
303+
}
304+
// If the path is `\X:\`, the leading separator was probably added by `windows_to_unix`
305+
// and we should get rid of it again.
306+
if path.get(2..4) == Some(&[b':'.into(), sep]) && path[0] == sep {
307+
// The new path is still absolute on Windows.
308+
path.remove(0);
309+
}
310+
// If this starts withs a `\` but not a `\\`, then this was absolute on Unix but is
311+
// relative on Windows (relative to "the root of the current directory", e.g. the
312+
// drive letter).
313+
else if path.first() == Some(&sep) && path.get(1) != Some(&sep) {
314+
// We add `\\?` so it starts with `\\?\` which is some magic path on Windows
315+
// that *is* considered absolute. This way we store the absolute Unix path
316+
// in something that looks like an absolute Windows path.
317+
path.splice(0..0, [sep, sep, b'?'.into()]);
318+
}
319+
}
320+
263321
// Below we assume that everything non-Windows works like Unix, at least
264322
// when it comes to file system path conventions.
265323
#[cfg(windows)]
@@ -268,102 +326,30 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
268326
os_str
269327
} else {
270328
// Unix target, Windows host.
271-
let (from, to) = match direction {
272-
PathConversion::HostToTarget => ('\\', '/'),
273-
PathConversion::TargetToHost => ('/', '\\'),
274-
};
275-
let mut converted = os_str
276-
.encode_wide()
277-
.map(|wchar| if wchar == from as u16 { to as u16 } else { wchar })
278-
.collect::<Vec<_>>();
279-
// We also have to ensure that absolute paths remain absolute.
329+
let mut path: Vec<u16> = os_str.encode_wide().collect();
280330
match direction {
281331
PathConversion::HostToTarget => {
282-
// If this is an absolute Windows path that starts with a drive letter (`C:/...`
283-
// after separator conversion), it would not be considered absolute by Unix
284-
// target code.
285-
if converted.get(1).copied() == Some(b':' as u16)
286-
&& converted.get(2).copied() == Some(b'/' as u16)
287-
{
288-
// We add a `/` at the beginning, to store the absolute Windows
289-
// path in something that looks like an absolute Unix path.
290-
converted.insert(0, b'/' as u16);
291-
}
332+
windows_to_unix(&mut path);
292333
}
293334
PathConversion::TargetToHost => {
294-
// If the path is `\C:\`, the leading backslash was probably added by the above code
295-
// and we should get rid of it again.
296-
if converted.get(0).copied() == Some(b'\\' as u16)
297-
&& converted.get(2).copied() == Some(b':' as u16)
298-
&& converted.get(3).copied() == Some(b'\\' as u16)
299-
{
300-
converted.remove(0);
301-
}
302-
// If the path starts with `\\`, it is a magic Windows path. Conveniently, paths
303-
// starting with `//` on Unix are also magic where the first component can have
304-
// "application-specific" meaning, which is reflected e.g. by `path::absolute`
305-
// leaving leading `//` alone (but normalizing leading `///` to `/`). So we
306-
// don't have to do anything, the magic Windows path should work mostly fine as
307-
// a magic Unix path.
335+
unix_to_windows(&mut path);
308336
}
309337
}
310-
Cow::Owned(OsString::from_wide(&converted))
338+
Cow::Owned(OsString::from_wide(&path))
311339
};
312340
#[cfg(unix)]
313341
return if target_os == "windows" {
314342
// Windows target, Unix host.
315-
let (from, to) = match direction {
316-
PathConversion::HostToTarget => (b'/', b'\\'),
317-
PathConversion::TargetToHost => (b'\\', b'/'),
318-
};
319-
let mut converted = os_str
320-
.as_bytes()
321-
.iter()
322-
.map(|&wchar| if wchar == from { to } else { wchar })
323-
.collect::<Vec<_>>();
324-
// We also have to ensure that absolute paths remain absolute.
343+
let mut path: Vec<u8> = os_str.into_owned().into_encoded_bytes();
325344
match direction {
326345
PathConversion::HostToTarget => {
327-
// If the path is `/C:/`, the leading backslash was probably added by the below
328-
// driver letter handling and we should get rid of it again.
329-
if converted.get(0).copied() == Some(b'\\')
330-
&& converted.get(2).copied() == Some(b':')
331-
&& converted.get(3).copied() == Some(b'\\')
332-
{
333-
converted.remove(0);
334-
}
335-
// If this starts withs a `\` but not a `\\`, then for Windows this is a
336-
// relative path (relative to "the root of the current directory", e.g. the
337-
// drive letter). But the host path on Unix is absolute as it starts with `/`.
338-
else if converted.get(0).copied() == Some(b'\\')
339-
&& converted.get(1).copied() != Some(b'\\')
340-
{
341-
// We add `\\?` so it starts with `\\?\` which is some magic path on Windows
342-
// that *is* considered absolute. This way we store the absolute host path
343-
// in something that looks like an absolute path to the (Windows) target.
344-
converted.splice(0..0, b"\\\\?".iter().copied());
345-
}
346+
unix_to_windows(&mut path);
346347
}
347348
PathConversion::TargetToHost => {
348-
// If this starts with `//?/`, it was probably produced by the above code and we
349-
// remove the `//?` that got added to get the Unix path back out.
350-
if converted.get(0).copied() == Some(b'/')
351-
&& converted.get(1).copied() == Some(b'/')
352-
&& converted.get(2).copied() == Some(b'?')
353-
&& converted.get(3).copied() == Some(b'/')
354-
{
355-
// Remove first 3 characters
356-
converted.splice(0..3, std::iter::empty());
357-
}
358-
// If it starts with a drive letter, convert it to an absolute Unix path.
359-
else if converted.get(1).copied() == Some(b':')
360-
&& converted.get(2).copied() == Some(b'/')
361-
{
362-
converted.insert(0, b'/');
363-
}
349+
windows_to_unix(&mut path);
364350
}
365351
}
366-
Cow::Owned(OsString::from_vec(converted))
352+
Cow::Owned(OsString::from_vec(path))
367353
} else {
368354
// Unix-on-Unix, all is fine.
369355
os_str

0 commit comments

Comments
 (0)