@@ -239,12 +239,143 @@ pub(crate) mod module {
239239 #[ cfg( target_env = "msvc" ) ]
240240 unsafe extern "C" {
241241 fn _wexecv ( cmdname : * const u16 , argv : * const * const u16 ) -> intptr_t ;
242+ fn _wexecve (
243+ cmdname : * const u16 ,
244+ argv : * const * const u16 ,
245+ envp : * const * const u16 ,
246+ ) -> intptr_t ;
247+ fn _wspawnv ( mode : i32 , cmdname : * const u16 , argv : * const * const u16 ) -> intptr_t ;
248+ fn _wspawnve (
249+ mode : i32 ,
250+ cmdname : * const u16 ,
251+ argv : * const * const u16 ,
252+ envp : * const * const u16 ,
253+ ) -> intptr_t ;
254+ }
255+
256+ #[ cfg( target_env = "msvc" ) ]
257+ #[ pyfunction]
258+ fn spawnv (
259+ mode : i32 ,
260+ path : OsPath ,
261+ argv : Either < PyListRef , PyTupleRef > ,
262+ vm : & VirtualMachine ,
263+ ) -> PyResult < intptr_t > {
264+ use std:: iter:: once;
265+
266+ let make_widestring =
267+ |s : & str | widestring:: WideCString :: from_os_str ( s) . map_err ( |err| err. to_pyexception ( vm) ) ;
268+
269+ let path = path. to_wide_cstring ( vm) ?;
270+
271+ let argv = vm. extract_elements_with ( argv. as_ref ( ) , |obj| {
272+ let arg = PyStrRef :: try_from_object ( vm, obj) ?;
273+ make_widestring ( arg. as_str ( ) )
274+ } ) ?;
275+
276+ let first = argv
277+ . first ( )
278+ . ok_or_else ( || vm. new_value_error ( "spawnv() arg 3 must not be empty" ) ) ?;
279+
280+ if first. is_empty ( ) {
281+ return Err ( vm. new_value_error ( "spawnv() arg 3 first element cannot be empty" ) ) ;
282+ }
283+
284+ let argv_spawn: Vec < * const u16 > = argv
285+ . iter ( )
286+ . map ( |v| v. as_ptr ( ) )
287+ . chain ( once ( std:: ptr:: null ( ) ) )
288+ . collect ( ) ;
289+
290+ let result = unsafe { suppress_iph ! ( _wspawnv( mode, path. as_ptr( ) , argv_spawn. as_ptr( ) ) ) } ;
291+ if result == -1 {
292+ Err ( errno_err ( vm) )
293+ } else {
294+ Ok ( result)
295+ }
296+ }
297+
298+ #[ cfg( target_env = "msvc" ) ]
299+ #[ pyfunction]
300+ fn spawnve (
301+ mode : i32 ,
302+ path : OsPath ,
303+ argv : Either < PyListRef , PyTupleRef > ,
304+ env : PyDictRef ,
305+ vm : & VirtualMachine ,
306+ ) -> PyResult < intptr_t > {
307+ use std:: iter:: once;
308+
309+ let make_widestring =
310+ |s : & str | widestring:: WideCString :: from_os_str ( s) . map_err ( |err| err. to_pyexception ( vm) ) ;
311+
312+ let path = path. to_wide_cstring ( vm) ?;
313+
314+ let argv = vm. extract_elements_with ( argv. as_ref ( ) , |obj| {
315+ let arg = PyStrRef :: try_from_object ( vm, obj) ?;
316+ make_widestring ( arg. as_str ( ) )
317+ } ) ?;
318+
319+ let first = argv
320+ . first ( )
321+ . ok_or_else ( || vm. new_value_error ( "spawnve() arg 2 cannot be empty" ) ) ?;
322+
323+ if first. is_empty ( ) {
324+ return Err ( vm. new_value_error ( "spawnve() arg 2 first element cannot be empty" ) ) ;
325+ }
326+
327+ let argv_spawn: Vec < * const u16 > = argv
328+ . iter ( )
329+ . map ( |v| v. as_ptr ( ) )
330+ . chain ( once ( std:: ptr:: null ( ) ) )
331+ . collect ( ) ;
332+
333+ // Build environment strings as "KEY=VALUE\0" wide strings
334+ let mut env_strings: Vec < widestring:: WideCString > = Vec :: new ( ) ;
335+ for ( key, value) in env. into_iter ( ) {
336+ let key = PyStrRef :: try_from_object ( vm, key) ?;
337+ let value = PyStrRef :: try_from_object ( vm, value) ?;
338+ let key_str = key. as_str ( ) ;
339+ let value_str = value. as_str ( ) ;
340+
341+ // Validate: no null characters in key or value
342+ if key_str. contains ( '\0' ) || value_str. contains ( '\0' ) {
343+ return Err ( vm. new_value_error ( "embedded null character" ) ) ;
344+ }
345+ // Validate: no '=' in key
346+ if key_str. contains ( '=' ) {
347+ return Err ( vm. new_value_error ( "illegal environment variable name" ) ) ;
348+ }
349+
350+ let env_str = format ! ( "{}={}" , key_str, value_str) ;
351+ env_strings. push ( make_widestring ( & env_str) ?) ;
352+ }
353+
354+ let envp: Vec < * const u16 > = env_strings
355+ . iter ( )
356+ . map ( |s| s. as_ptr ( ) )
357+ . chain ( once ( std:: ptr:: null ( ) ) )
358+ . collect ( ) ;
359+
360+ let result = unsafe {
361+ suppress_iph ! ( _wspawnve(
362+ mode,
363+ path. as_ptr( ) ,
364+ argv_spawn. as_ptr( ) ,
365+ envp. as_ptr( )
366+ ) )
367+ } ;
368+ if result == -1 {
369+ Err ( errno_err ( vm) )
370+ } else {
371+ Ok ( result)
372+ }
242373 }
243374
244375 #[ cfg( target_env = "msvc" ) ]
245376 #[ pyfunction]
246377 fn execv (
247- path : PyStrRef ,
378+ path : OsPath ,
248379 argv : Either < PyListRef , PyTupleRef > ,
249380 vm : & VirtualMachine ,
250381 ) -> PyResult < ( ) > {
@@ -253,7 +384,7 @@ pub(crate) mod module {
253384 let make_widestring =
254385 |s : & str | widestring:: WideCString :: from_os_str ( s) . map_err ( |err| err. to_pyexception ( vm) ) ;
255386
256- let path = make_widestring ( path. as_str ( ) ) ?;
387+ let path = path. to_wide_cstring ( vm ) ?;
257388
258389 let argv = vm. extract_elements_with ( argv. as_ref ( ) , |obj| {
259390 let arg = PyStrRef :: try_from_object ( vm, obj) ?;
@@ -281,6 +412,76 @@ pub(crate) mod module {
281412 }
282413 }
283414
415+ #[ cfg( target_env = "msvc" ) ]
416+ #[ pyfunction]
417+ fn execve (
418+ path : OsPath ,
419+ argv : Either < PyListRef , PyTupleRef > ,
420+ env : PyDictRef ,
421+ vm : & VirtualMachine ,
422+ ) -> PyResult < ( ) > {
423+ use std:: iter:: once;
424+
425+ let make_widestring =
426+ |s : & str | widestring:: WideCString :: from_os_str ( s) . map_err ( |err| err. to_pyexception ( vm) ) ;
427+
428+ let path = path. to_wide_cstring ( vm) ?;
429+
430+ let argv = vm. extract_elements_with ( argv. as_ref ( ) , |obj| {
431+ let arg = PyStrRef :: try_from_object ( vm, obj) ?;
432+ make_widestring ( arg. as_str ( ) )
433+ } ) ?;
434+
435+ let first = argv
436+ . first ( )
437+ . ok_or_else ( || vm. new_value_error ( "execve: argv must not be empty" ) ) ?;
438+
439+ if first. is_empty ( ) {
440+ return Err ( vm. new_value_error ( "execve: argv first element cannot be empty" ) ) ;
441+ }
442+
443+ let argv_execve: Vec < * const u16 > = argv
444+ . iter ( )
445+ . map ( |v| v. as_ptr ( ) )
446+ . chain ( once ( std:: ptr:: null ( ) ) )
447+ . collect ( ) ;
448+
449+ // Build environment strings as "KEY=VALUE\0" wide strings
450+ let mut env_strings: Vec < widestring:: WideCString > = Vec :: new ( ) ;
451+ for ( key, value) in env. into_iter ( ) {
452+ let key = PyStrRef :: try_from_object ( vm, key) ?;
453+ let value = PyStrRef :: try_from_object ( vm, value) ?;
454+ let key_str = key. as_str ( ) ;
455+ let value_str = value. as_str ( ) ;
456+
457+ // Validate: no null characters in key or value
458+ if key_str. contains ( '\0' ) || value_str. contains ( '\0' ) {
459+ return Err ( vm. new_value_error ( "embedded null character" ) ) ;
460+ }
461+ // Validate: no '=' in key
462+ if key_str. contains ( '=' ) {
463+ return Err ( vm. new_value_error ( "illegal environment variable name" ) ) ;
464+ }
465+
466+ let env_str = format ! ( "{}={}" , key_str, value_str) ;
467+ env_strings. push ( make_widestring ( & env_str) ?) ;
468+ }
469+
470+ let envp: Vec < * const u16 > = env_strings
471+ . iter ( )
472+ . map ( |s| s. as_ptr ( ) )
473+ . chain ( once ( std:: ptr:: null ( ) ) )
474+ . collect ( ) ;
475+
476+ if ( unsafe { suppress_iph ! ( _wexecve( path. as_ptr( ) , argv_execve. as_ptr( ) , envp. as_ptr( ) ) ) }
477+ == -1 )
478+ {
479+ Err ( errno_err ( vm) )
480+ } else {
481+ Ok ( ( ) )
482+ }
483+ }
484+
284485 #[ pyfunction]
285486 fn _getfinalpathname ( path : OsPath , vm : & VirtualMachine ) -> PyResult {
286487 let real = path
0 commit comments