@@ -299,3 +299,89 @@ pub(crate) fn append_arg(cmd: &mut Vec<u16>, arg: &Arg, force_quotes: bool) -> i
299299 }
300300 Ok ( ( ) )
301301}
302+
303+ pub ( crate ) fn make_bat_command_line (
304+ script : & [ u16 ] ,
305+ args : & [ Arg ] ,
306+ force_quotes : bool ,
307+ ) -> io:: Result < Vec < u16 > > {
308+ // Set the start of the command line to `cmd.exe /c "`
309+ // It is necessary to surround the command in an extra pair of quotes,
310+ // hence The trailing quote here. It will be closed after all arguments
311+ // have been added.
312+ let mut cmd: Vec < u16 > = "cmd.exe /c \" " . encode_utf16 ( ) . collect ( ) ;
313+
314+ // Push the script name surrounded by its quote pair.
315+ cmd. push ( b'"' as u16 ) ;
316+ cmd. extend_from_slice ( script. strip_suffix ( & [ 0 ] ) . unwrap_or ( script) ) ;
317+ cmd. push ( b'"' as u16 ) ;
318+
319+ // Append the arguments.
320+ // FIXME: This needs tests to ensure that the arguments are properly
321+ // reconstructed by the batch script by default.
322+ for arg in args {
323+ cmd. push ( ' ' as u16 ) ;
324+ append_arg ( & mut cmd, arg, force_quotes) ?;
325+ }
326+
327+ // Close the quote we left opened earlier.
328+ cmd. push ( b'"' as u16 ) ;
329+
330+ Ok ( cmd)
331+ }
332+
333+ /// Takes a path and tries to return a non-verbatim path.
334+ ///
335+ /// This is necessary because cmd.exe does not support verbatim paths.
336+ pub ( crate ) fn to_user_path ( mut path : Vec < u16 > ) -> io:: Result < Vec < u16 > > {
337+ use crate :: ptr;
338+ use crate :: sys:: windows:: fill_utf16_buf;
339+
340+ // UTF-16 encoded code points, used in parsing and building UTF-16 paths.
341+ // All of these are in the ASCII range so they can be cast directly to `u16`.
342+ const SEP : u16 = b'\\' as _ ;
343+ const QUERY : u16 = b'?' as _ ;
344+ const COLON : u16 = b':' as _ ;
345+ const U : u16 = b'U' as _ ;
346+ const N : u16 = b'N' as _ ;
347+ const C : u16 = b'C' as _ ;
348+
349+ // Early return if the path is too long to remove the verbatim prefix.
350+ const LEGACY_MAX_PATH : usize = 260 ;
351+ if path. len ( ) > LEGACY_MAX_PATH {
352+ return Ok ( path) ;
353+ }
354+
355+ match & path[ ..] {
356+ // `\\?\C:\...` => `C:\...`
357+ [ SEP , SEP , QUERY , SEP , _, COLON , SEP , ..] => unsafe {
358+ let lpfilename = path[ 4 ..] . as_ptr ( ) ;
359+ fill_utf16_buf (
360+ |buffer, size| c:: GetFullPathNameW ( lpfilename, size, buffer, ptr:: null_mut ( ) ) ,
361+ |full_path : & [ u16 ] | {
362+ if full_path == & path[ 4 ..path. len ( ) - 1 ] { full_path. into ( ) } else { path }
363+ } ,
364+ )
365+ } ,
366+ // `\\?\UNC\...` => `\\...`
367+ [ SEP , SEP , QUERY , SEP , U , N , C , SEP , ..] => unsafe {
368+ // Change the `C` in `UNC\` to `\` so we can get a slice that starts with `\\`.
369+ path[ 6 ] = b'\\' as u16 ;
370+ let lpfilename = path[ 6 ..] . as_ptr ( ) ;
371+ fill_utf16_buf (
372+ |buffer, size| c:: GetFullPathNameW ( lpfilename, size, buffer, ptr:: null_mut ( ) ) ,
373+ |full_path : & [ u16 ] | {
374+ if full_path == & path[ 6 ..path. len ( ) - 1 ] {
375+ full_path. into ( )
376+ } else {
377+ // Restore the 'C' in "UNC".
378+ path[ 6 ] = b'C' as u16 ;
379+ path
380+ }
381+ } ,
382+ )
383+ } ,
384+ // For everything else, leave the path unchanged.
385+ _ => Ok ( path) ,
386+ }
387+ }
0 commit comments