@@ -263,12 +263,143 @@ pub(crate) mod module {
263263 #[ cfg( target_env = "msvc" ) ]
264264 unsafe extern "C" {
265265 fn _wexecv ( cmdname : * const u16 , argv : * const * const u16 ) -> intptr_t ;
266+ fn _wexecve (
267+ cmdname : * const u16 ,
268+ argv : * const * const u16 ,
269+ envp : * const * const u16 ,
270+ ) -> intptr_t ;
271+ fn _wspawnv ( mode : i32 , cmdname : * const u16 , argv : * const * const u16 ) -> intptr_t ;
272+ fn _wspawnve (
273+ mode : i32 ,
274+ cmdname : * const u16 ,
275+ argv : * const * const u16 ,
276+ envp : * const * const u16 ,
277+ ) -> intptr_t ;
278+ }
279+
280+ #[ cfg( target_env = "msvc" ) ]
281+ #[ pyfunction]
282+ fn spawnv (
283+ mode : i32 ,
284+ path : OsPath ,
285+ argv : Either < PyListRef , PyTupleRef > ,
286+ vm : & VirtualMachine ,
287+ ) -> PyResult < intptr_t > {
288+ use std:: iter:: once;
289+
290+ let make_widestring =
291+ |s : & str | widestring:: WideCString :: from_os_str ( s) . map_err ( |err| err. to_pyexception ( vm) ) ;
292+
293+ let path = path. to_wide_cstring ( vm) ?;
294+
295+ let argv = vm. extract_elements_with ( argv. as_ref ( ) , |obj| {
296+ let arg = PyStrRef :: try_from_object ( vm, obj) ?;
297+ make_widestring ( arg. as_str ( ) )
298+ } ) ?;
299+
300+ let first = argv
301+ . first ( )
302+ . ok_or_else ( || vm. new_value_error ( "spawnv() arg 3 must not be empty" ) ) ?;
303+
304+ if first. is_empty ( ) {
305+ return Err ( vm. new_value_error ( "spawnv() arg 3 first element cannot be empty" ) ) ;
306+ }
307+
308+ let argv_spawn: Vec < * const u16 > = argv
309+ . iter ( )
310+ . map ( |v| v. as_ptr ( ) )
311+ . chain ( once ( std:: ptr:: null ( ) ) )
312+ . collect ( ) ;
313+
314+ let result = unsafe { suppress_iph ! ( _wspawnv( mode, path. as_ptr( ) , argv_spawn. as_ptr( ) ) ) } ;
315+ if result == -1 {
316+ Err ( errno_err ( vm) )
317+ } else {
318+ Ok ( result)
319+ }
320+ }
321+
322+ #[ cfg( target_env = "msvc" ) ]
323+ #[ pyfunction]
324+ fn spawnve (
325+ mode : i32 ,
326+ path : OsPath ,
327+ argv : Either < PyListRef , PyTupleRef > ,
328+ env : PyDictRef ,
329+ vm : & VirtualMachine ,
330+ ) -> PyResult < intptr_t > {
331+ use std:: iter:: once;
332+
333+ let make_widestring =
334+ |s : & str | widestring:: WideCString :: from_os_str ( s) . map_err ( |err| err. to_pyexception ( vm) ) ;
335+
336+ let path = path. to_wide_cstring ( vm) ?;
337+
338+ let argv = vm. extract_elements_with ( argv. as_ref ( ) , |obj| {
339+ let arg = PyStrRef :: try_from_object ( vm, obj) ?;
340+ make_widestring ( arg. as_str ( ) )
341+ } ) ?;
342+
343+ let first = argv
344+ . first ( )
345+ . ok_or_else ( || vm. new_value_error ( "spawnve() arg 2 cannot be empty" ) ) ?;
346+
347+ if first. is_empty ( ) {
348+ return Err ( vm. new_value_error ( "spawnve() arg 2 first element cannot be empty" ) ) ;
349+ }
350+
351+ let argv_spawn: Vec < * const u16 > = argv
352+ . iter ( )
353+ . map ( |v| v. as_ptr ( ) )
354+ . chain ( once ( std:: ptr:: null ( ) ) )
355+ . collect ( ) ;
356+
357+ // Build environment strings as "KEY=VALUE\0" wide strings
358+ let mut env_strings: Vec < widestring:: WideCString > = Vec :: new ( ) ;
359+ for ( key, value) in env. into_iter ( ) {
360+ let key = PyStrRef :: try_from_object ( vm, key) ?;
361+ let value = PyStrRef :: try_from_object ( vm, value) ?;
362+ let key_str = key. as_str ( ) ;
363+ let value_str = value. as_str ( ) ;
364+
365+ // Validate: no null characters in key or value
366+ if key_str. contains ( '\0' ) || value_str. contains ( '\0' ) {
367+ return Err ( vm. new_value_error ( "embedded null character" ) ) ;
368+ }
369+ // Validate: no '=' in key
370+ if key_str. contains ( '=' ) {
371+ return Err ( vm. new_value_error ( "illegal environment variable name" ) ) ;
372+ }
373+
374+ let env_str = format ! ( "{}={}" , key_str, value_str) ;
375+ env_strings. push ( make_widestring ( & env_str) ?) ;
376+ }
377+
378+ let envp: Vec < * const u16 > = env_strings
379+ . iter ( )
380+ . map ( |s| s. as_ptr ( ) )
381+ . chain ( once ( std:: ptr:: null ( ) ) )
382+ . collect ( ) ;
383+
384+ let result = unsafe {
385+ suppress_iph ! ( _wspawnve(
386+ mode,
387+ path. as_ptr( ) ,
388+ argv_spawn. as_ptr( ) ,
389+ envp. as_ptr( )
390+ ) )
391+ } ;
392+ if result == -1 {
393+ Err ( errno_err ( vm) )
394+ } else {
395+ Ok ( result)
396+ }
266397 }
267398
268399 #[ cfg( target_env = "msvc" ) ]
269400 #[ pyfunction]
270401 fn execv (
271- path : PyStrRef ,
402+ path : OsPath ,
272403 argv : Either < PyListRef , PyTupleRef > ,
273404 vm : & VirtualMachine ,
274405 ) -> PyResult < ( ) > {
@@ -277,7 +408,7 @@ pub(crate) mod module {
277408 let make_widestring =
278409 |s : & str | widestring:: WideCString :: from_os_str ( s) . map_err ( |err| err. to_pyexception ( vm) ) ;
279410
280- let path = make_widestring ( path. as_str ( ) ) ?;
411+ let path = path. to_wide_cstring ( vm ) ?;
281412
282413 let argv = vm. extract_elements_with ( argv. as_ref ( ) , |obj| {
283414 let arg = PyStrRef :: try_from_object ( vm, obj) ?;
@@ -305,6 +436,76 @@ pub(crate) mod module {
305436 }
306437 }
307438
439+ #[ cfg( target_env = "msvc" ) ]
440+ #[ pyfunction]
441+ fn execve (
442+ path : OsPath ,
443+ argv : Either < PyListRef , PyTupleRef > ,
444+ env : PyDictRef ,
445+ vm : & VirtualMachine ,
446+ ) -> PyResult < ( ) > {
447+ use std:: iter:: once;
448+
449+ let make_widestring =
450+ |s : & str | widestring:: WideCString :: from_os_str ( s) . map_err ( |err| err. to_pyexception ( vm) ) ;
451+
452+ let path = path. to_wide_cstring ( vm) ?;
453+
454+ let argv = vm. extract_elements_with ( argv. as_ref ( ) , |obj| {
455+ let arg = PyStrRef :: try_from_object ( vm, obj) ?;
456+ make_widestring ( arg. as_str ( ) )
457+ } ) ?;
458+
459+ let first = argv
460+ . first ( )
461+ . ok_or_else ( || vm. new_value_error ( "execve: argv must not be empty" ) ) ?;
462+
463+ if first. is_empty ( ) {
464+ return Err ( vm. new_value_error ( "execve: argv first element cannot be empty" ) ) ;
465+ }
466+
467+ let argv_execve: Vec < * const u16 > = argv
468+ . iter ( )
469+ . map ( |v| v. as_ptr ( ) )
470+ . chain ( once ( std:: ptr:: null ( ) ) )
471+ . collect ( ) ;
472+
473+ // Build environment strings as "KEY=VALUE\0" wide strings
474+ let mut env_strings: Vec < widestring:: WideCString > = Vec :: new ( ) ;
475+ for ( key, value) in env. into_iter ( ) {
476+ let key = PyStrRef :: try_from_object ( vm, key) ?;
477+ let value = PyStrRef :: try_from_object ( vm, value) ?;
478+ let key_str = key. as_str ( ) ;
479+ let value_str = value. as_str ( ) ;
480+
481+ // Validate: no null characters in key or value
482+ if key_str. contains ( '\0' ) || value_str. contains ( '\0' ) {
483+ return Err ( vm. new_value_error ( "embedded null character" ) ) ;
484+ }
485+ // Validate: no '=' in key
486+ if key_str. contains ( '=' ) {
487+ return Err ( vm. new_value_error ( "illegal environment variable name" ) ) ;
488+ }
489+
490+ let env_str = format ! ( "{}={}" , key_str, value_str) ;
491+ env_strings. push ( make_widestring ( & env_str) ?) ;
492+ }
493+
494+ let envp: Vec < * const u16 > = env_strings
495+ . iter ( )
496+ . map ( |s| s. as_ptr ( ) )
497+ . chain ( once ( std:: ptr:: null ( ) ) )
498+ . collect ( ) ;
499+
500+ if ( unsafe { suppress_iph ! ( _wexecve( path. as_ptr( ) , argv_execve. as_ptr( ) , envp. as_ptr( ) ) ) }
501+ == -1 )
502+ {
503+ Err ( errno_err ( vm) )
504+ } else {
505+ Ok ( ( ) )
506+ }
507+ }
508+
308509 #[ pyfunction]
309510 fn _getfinalpathname ( path : OsPath , vm : & VirtualMachine ) -> PyResult {
310511 let real = path
0 commit comments