@@ -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