Michele Locati noticed that executing programs on Windows has an additional
complexity that does not exist on Unix: On Unix, programs have their runtime
path embedded in the executable ('-rpath' option). On Windows, this is not
the case; instead, DLLs used by the executable are looked up [1]
  - in the directory where the executable sits,
  - in $PATH,
  - in the current directory (this is irrelevant here).

As a consequence, GNU programs on Windows don't install DLLs in $(libdir),
but in $(bindir), so that executables installed in $(bindir) will find them.

But there is a problem when a program (installed in $(bindir)) invokes a
program installed in $(pkglibexecdir). Namely, it will not find its DLLs,
if $(bindir) does not happen to be in the $PATH.

What workarounds exist?

  - A package could install all its executables in $(bindir), none in
    $(pkglibexecdir).
    Problem: Executables from $(pkglibexecdir) are not meant to be public
    (i.e. visible to the user).

  - A package could install the DLLs both in $(bindir) and $(pkglibexecdir).
    Problem: Waste of disk space.

  - A package could avoid invoking helper executables and instead incorporate
    the helper code in its own executable or libraries.
    Problem: A particular platform would dictate the architecture of the
    entire package.

  - While invoking a program installed in $(pkglibexecdir), $(bindir) gets
    added to $PATH.
    This is the best solution.
    Note that it cannot be implemented by invoking "env PATH=.... program ...",
    because there is no 'env' program on native Windows.
    Note also that we cannot require the caller to do a
      setenv ("PATH", value, 1);
    call, because setenv() is not multithread-safe and also because it would
    be an ad-hoc "solution".

So, the PATH modifications must be done in the Gnulib modules 'execute'
and 'spawn-pipe'. (The posix_spawn and posix_spawnp functions don't need
modifications, since they already accept an 'envp' parameter which can
contain a definition of PATH.)

This patch implements that.

[1] 
https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order


2024-10-22  Bruno Haible  <br...@clisp.org>

        execute, spawn-pipe: Support DLL dependencies of Windows executables.
        Reported by Michele Locati <mich...@locati.it>.
        * lib/windows-path.h: New file.
        * lib/windows-path.c: New file.
        * lib/windows-spawn.h (compose_envblock): Add new_PATH parameter.
        (spawnpvech): Add dll_dirs parameter. Call extended_PATH.
        * lib/windows-spawn.c: Include windows-path.h.
        (compose_envblock): Add new_PATH parameter.
        * modules/windows-spawn (Description): Now applies to Cygwin as well.
        (Files): Add lib/windows-path.h, lib/windows-path.c.
        (configure.ac): Define GL_COND_OBJ_WINDOWS_PATH.
        (Makefile.am): Conditionally compile windows-path.c.
        (Include): Add windows-path.h.
        * lib/spawni.c (__spawni): Update compose_envblock call.
        * lib/execute.h (execute): Add dll_dirs parameter.
        * lib/execute.c: Include windows-path.h.
        (execute): Add dll_dirs parameter. Pass it down to spawnpvech. Call
        extended_environ.
        * lib/spawn-pipe.h (create_pipe_out, create_pipe_in, create_pipe_bidi):
        Add dll_dirs parameter.
        * lib/spawn-pipe.c: Include windows-path.h.
        (create_pipe): Add dll_dirs parameter. Pass it down to spawnpvech. Call
        extended_environ.
        (create_pipe_bidi, create_pipe_in, create_pipe_out): Add dll_dirs
        parameter.
        * lib/javaexec.c (execute_java_class): Update execute invocations.
        * lib/cygpath.c (execute_and_read_line): Update create_pipe_in
        invocation.
        * lib/javaversion.c (execute_and_read_line): Likewise.
        * lib/csharpcomp.c (compile_csharp_using_mono,
        compile_csharp_using_dotnet, compile_csharp_using_sscli): Update
        execute, create_pipe_in invocations.
        * lib/csharpexec.c (execute_csharp_using_mono,
        execute_csharp_using_dotnet, execute_csharp_using_sscli): Likewise.
        * lib/javacomp.c (compile_using_envjavac, compile_using_javac,
        execute_and_read_line, is_javac_present): Likewise.
        * lib/pipe-filter-gi.c (pipe_filter_gi_create): Update create_pipe_bidi
        invocation.
        * lib/pipe-filter-ii.c (pipe_filter_ii_execute): Likewise.
        * tests/test-execute-main.c (main): Update execute invocations.
        * tests/test-execute-script.c (main): Likewise.
        * tests/test-spawn-pipe-main.c (main): Update create_pipe_bidi
        invocation.
        * tests/test-spawn-pipe-script.c (main): Update create_pipe_in
        invocations.
        * NEWS: Mention the changes.

>From 427249c61666dcf649e6ba18fb327cdf71c45780 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Tue, 22 Oct 2024 22:08:24 +0200
Subject: [PATCH] execute, spawn-pipe: Support DLL dependencies of Windows
 executables.

Reported by Michele Locati <mich...@locati.it>.

* lib/windows-path.h: New file.
* lib/windows-path.c: New file.
* lib/windows-spawn.h (compose_envblock): Add new_PATH parameter.
(spawnpvech): Add dll_dirs parameter. Call extended_PATH.
* lib/windows-spawn.c: Include windows-path.h.
(compose_envblock): Add new_PATH parameter.
* modules/windows-spawn (Description): Now applies to Cygwin as well.
(Files): Add lib/windows-path.h, lib/windows-path.c.
(configure.ac): Define GL_COND_OBJ_WINDOWS_PATH.
(Makefile.am): Conditionally compile windows-path.c.
(Include): Add windows-path.h.
* lib/spawni.c (__spawni): Update compose_envblock call.
* lib/execute.h (execute): Add dll_dirs parameter.
* lib/execute.c: Include windows-path.h.
(execute): Add dll_dirs parameter. Pass it down to spawnpvech. Call
extended_environ.
* lib/spawn-pipe.h (create_pipe_out, create_pipe_in, create_pipe_bidi):
Add dll_dirs parameter.
* lib/spawn-pipe.c: Include windows-path.h.
(create_pipe): Add dll_dirs parameter. Pass it down to spawnpvech. Call
extended_environ.
(create_pipe_bidi, create_pipe_in, create_pipe_out): Add dll_dirs
parameter.
* lib/javaexec.c (execute_java_class): Update execute invocations.
* lib/cygpath.c (execute_and_read_line): Update create_pipe_in
invocation.
* lib/javaversion.c (execute_and_read_line): Likewise.
* lib/csharpcomp.c (compile_csharp_using_mono,
compile_csharp_using_dotnet, compile_csharp_using_sscli): Update
execute, create_pipe_in invocations.
* lib/csharpexec.c (execute_csharp_using_mono,
execute_csharp_using_dotnet, execute_csharp_using_sscli): Likewise.
* lib/javacomp.c (compile_using_envjavac, compile_using_javac,
execute_and_read_line, is_javac_present): Likewise.
* lib/pipe-filter-gi.c (pipe_filter_gi_create): Update create_pipe_bidi
invocation.
* lib/pipe-filter-ii.c (pipe_filter_ii_execute): Likewise.
* tests/test-execute-main.c (main): Update execute invocations.
* tests/test-execute-script.c (main): Likewise.
* tests/test-spawn-pipe-main.c (main): Update create_pipe_bidi
invocation.
* tests/test-spawn-pipe-script.c (main): Update create_pipe_in
invocations.
* NEWS: Mention the changes.
---
 ChangeLog                      |  49 +++++++++++
 NEWS                           |  11 +++
 lib/csharpcomp.c               |  22 ++---
 lib/csharpexec.c               |   8 +-
 lib/cygpath.c                  |   2 +-
 lib/execute.c                  |  40 +++++++--
 lib/execute.h                  |  11 +++
 lib/javacomp.c                 |   8 +-
 lib/javaexec.c                 |   4 +-
 lib/javaversion.c              |   2 +-
 lib/pipe-filter-gi.c           |   2 +-
 lib/pipe-filter-ii.c           |   2 +-
 lib/spawn-pipe.c               |  54 ++++++++++--
 lib/spawn-pipe.h               |   7 ++
 lib/spawni.c                   |   2 +-
 lib/windows-path.c             | 146 +++++++++++++++++++++++++++++++++
 lib/windows-path.h             |  54 ++++++++++++
 lib/windows-spawn.c            |  83 ++++++++++++-------
 lib/windows-spawn.h            |   8 +-
 modules/windows-spawn          |  11 ++-
 tests/test-execute-main.c      |  44 +++++-----
 tests/test-execute-script.c    |   4 +-
 tests/test-spawn-pipe-main.c   |   2 +-
 tests/test-spawn-pipe-script.c |   4 +-
 24 files changed, 482 insertions(+), 98 deletions(-)
 create mode 100644 lib/windows-path.c
 create mode 100644 lib/windows-path.h

diff --git a/ChangeLog b/ChangeLog
index a9f4da9556..f87126240a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,52 @@
+2024-10-22  Bruno Haible  <br...@clisp.org>
+
+	execute, spawn-pipe: Support DLL dependencies of Windows executables.
+	Reported by Michele Locati <mich...@locati.it>.
+	* lib/windows-path.h: New file.
+	* lib/windows-path.c: New file.
+	* lib/windows-spawn.h (compose_envblock): Add new_PATH parameter.
+	(spawnpvech): Add dll_dirs parameter. Call extended_PATH.
+	* lib/windows-spawn.c: Include windows-path.h.
+	(compose_envblock): Add new_PATH parameter.
+	* modules/windows-spawn (Description): Now applies to Cygwin as well.
+	(Files): Add lib/windows-path.h, lib/windows-path.c.
+	(configure.ac): Define GL_COND_OBJ_WINDOWS_PATH.
+	(Makefile.am): Conditionally compile windows-path.c.
+	(Include): Add windows-path.h.
+	* lib/spawni.c (__spawni): Update compose_envblock call.
+	* lib/execute.h (execute): Add dll_dirs parameter.
+	* lib/execute.c: Include windows-path.h.
+	(execute): Add dll_dirs parameter. Pass it down to spawnpvech. Call
+	extended_environ.
+	* lib/spawn-pipe.h (create_pipe_out, create_pipe_in, create_pipe_bidi):
+	Add dll_dirs parameter.
+	* lib/spawn-pipe.c: Include windows-path.h.
+	(create_pipe): Add dll_dirs parameter. Pass it down to spawnpvech. Call
+	extended_environ.
+	(create_pipe_bidi, create_pipe_in, create_pipe_out): Add dll_dirs
+	parameter.
+	* lib/javaexec.c (execute_java_class): Update execute invocations.
+	* lib/cygpath.c (execute_and_read_line): Update create_pipe_in
+	invocation.
+	* lib/javaversion.c (execute_and_read_line): Likewise.
+	* lib/csharpcomp.c (compile_csharp_using_mono,
+	compile_csharp_using_dotnet, compile_csharp_using_sscli): Update
+	execute, create_pipe_in invocations.
+	* lib/csharpexec.c (execute_csharp_using_mono,
+	execute_csharp_using_dotnet, execute_csharp_using_sscli): Likewise.
+	* lib/javacomp.c (compile_using_envjavac, compile_using_javac,
+	execute_and_read_line, is_javac_present): Likewise.
+	* lib/pipe-filter-gi.c (pipe_filter_gi_create): Update create_pipe_bidi
+	invocation.
+	* lib/pipe-filter-ii.c (pipe_filter_ii_execute): Likewise.
+	* tests/test-execute-main.c (main): Update execute invocations.
+	* tests/test-execute-script.c (main): Likewise.
+	* tests/test-spawn-pipe-main.c (main): Update create_pipe_bidi
+	invocation.
+	* tests/test-spawn-pipe-script.c (main): Update create_pipe_in
+	invocations.
+	* NEWS: Mention the changes.
+
 2024-10-21  Bruno Haible  <br...@clisp.org>
 
 	reallocarray: Don't assume unportable behaviour of realloc.
diff --git a/NEWS b/NEWS
index bd120be0b3..7c48798dbf 100644
--- a/NEWS
+++ b/NEWS
@@ -74,6 +74,17 @@ User visible incompatible changes
 
 Date        Modules         Changes
 
+2024-10-22  spawn-pipe      The functions 'create_pipe_out', 'create_pipe_in',
+                            'create_pipe_bidi' now take a 4th argument
+                            'const char * const *dll_dirs'. To maintain the
+                            previous behaviour, insert NULL as additional 4th
+                            argument.
+
+2024-10-22  execute         The function 'execute' now takes a 4th argument
+                            'const char * const *dll_dirs'. To maintain the
+                            previous behaviour, insert NULL as additional 4th
+                            argument.
+
 2024-10-02  file-has-acl    The file_has_aclinfo function introduced 3 days ago
                             now has a different signature.
 
diff --git a/lib/csharpcomp.c b/lib/csharpcomp.c
index 4874061e60..f0a941fd26 100644
--- a/lib/csharpcomp.c
+++ b/lib/csharpcomp.c
@@ -106,7 +106,7 @@ compile_csharp_using_mono (const char * const *sources,
       argv[0] = "mcs";
       argv[1] = "--version";
       argv[2] = NULL;
-      child = create_pipe_in ("mcs", "mcs", argv, NULL,
+      child = create_pipe_in ("mcs", "mcs", argv, NULL, NULL,
                               DEV_NULL, true, true, false, fd);
       mcs_present = false;
       if (child != -1)
@@ -215,7 +215,7 @@ compile_csharp_using_mono (const char * const *sources,
           free (command);
         }
 
-      child = create_pipe_in ("mcs", "mcs", argv, NULL,
+      child = create_pipe_in ("mcs", "mcs", argv, NULL, NULL,
                               NULL, false, true, true, fd);
 
       /* Read the subprocess output, copying it to stderr.  Drop the last
@@ -292,7 +292,7 @@ compile_csharp_using_dotnet (const char * const *sources,
         argv[0] = "dotnet";
         argv[1] = "--list-runtimes";
         argv[2] = NULL;
-        exitstatus = execute ("dotnet", "dotnet", argv, NULL,
+        exitstatus = execute ("dotnet", "dotnet", argv, NULL, NULL,
                               false, false, true, true,
                               true, false, NULL);
       }
@@ -305,7 +305,7 @@ compile_csharp_using_dotnet (const char * const *sources,
           argv[0] = "dotnet";
           argv[1] = "--list-sdks";
           argv[2] = NULL;
-          child = create_pipe_in ("dotnet", "dotnet", argv, NULL,
+          child = create_pipe_in ("dotnet", "dotnet", argv, NULL, NULL,
                                   DEV_NULL, true, true, false, fd);
           if (child != -1)
             {
@@ -353,7 +353,7 @@ compile_csharp_using_dotnet (const char * const *sources,
 
         /* Open a pipe to the program.  */
         int fd[1];
-        pid_t child = create_pipe_in ("dotnet", "dotnet", argv, NULL,
+        pid_t child = create_pipe_in ("dotnet", "dotnet", argv, NULL, NULL,
                                       DEV_NULL, false, true, false, fd);
         if (child == -1)
           {
@@ -445,7 +445,7 @@ compile_csharp_using_dotnet (const char * const *sources,
 
         /* Open a pipe to the program.  */
         int fd[1];
-        pid_t child = create_pipe_in ("dotnet", "dotnet", argv, NULL,
+        pid_t child = create_pipe_in ("dotnet", "dotnet", argv, NULL, NULL,
                                       DEV_NULL, false, true, false, fd);
         if (child == -1)
           {
@@ -651,7 +651,7 @@ compile_csharp_using_dotnet (const char * const *sources,
           free (command);
         }
 
-      exitstatus = execute ("dotnet", "dotnet", argv, NULL,
+      exitstatus = execute ("dotnet", "dotnet", argv, NULL, NULL,
                             false, false, false, false,
                             true, true, NULL);
 
@@ -691,7 +691,7 @@ compile_csharp_using_dotnet (const char * const *sources,
           argv[0] = "csc";
           argv[1] = "-help";
           argv[2] = NULL;
-          child = create_pipe_in ("csc", "csc", argv, NULL,
+          child = create_pipe_in ("csc", "csc", argv, NULL, NULL,
                                   DEV_NULL, true, true, false, fd);
           if (child != -1)
             {
@@ -825,7 +825,7 @@ compile_csharp_using_dotnet (const char * const *sources,
               free (command);
             }
 
-          exitstatus = execute ("csc", "csc", argv, NULL,
+          exitstatus = execute ("csc", "csc", argv, NULL, NULL,
                                 false, false, false, false,
                                 true, true, NULL);
 
@@ -870,7 +870,7 @@ compile_csharp_using_sscli (const char * const *sources,
       argv[0] = "csc";
       argv[1] = "-help";
       argv[2] = NULL;
-      child = create_pipe_in ("csc", "csc", argv, NULL,
+      child = create_pipe_in ("csc", "csc", argv, NULL, NULL,
                               DEV_NULL, true, true, false, fd);
       csc_present = false;
       if (child != -1)
@@ -997,7 +997,7 @@ compile_csharp_using_sscli (const char * const *sources,
           free (command);
         }
 
-      exitstatus = execute ("csc", "csc", argv, NULL,
+      exitstatus = execute ("csc", "csc", argv, NULL, NULL,
                             false, false, false, false,
                             true, true, NULL);
 
diff --git a/lib/csharpexec.c b/lib/csharpexec.c
index 79f2a3fd91..c0658da5a8 100644
--- a/lib/csharpexec.c
+++ b/lib/csharpexec.c
@@ -132,7 +132,7 @@ execute_csharp_using_mono (const char *assembly_path,
       argv[0] = "mono";
       argv[1] = "--version";
       argv[2] = NULL;
-      exitstatus = execute ("mono", "mono", argv, NULL,
+      exitstatus = execute ("mono", "mono", argv, NULL, NULL,
                             false, false, true, true,
                             true, false, NULL);
       mono_present = (exitstatus == 0);
@@ -196,7 +196,7 @@ execute_csharp_using_dotnet (const char *assembly_path,
       argv[0] = "dotnet";
       argv[1] = "--list-runtimes";
       argv[2] = NULL;
-      exitstatus = execute ("dotnet", "dotnet", argv, NULL,
+      exitstatus = execute ("dotnet", "dotnet", argv, NULL, NULL,
                             false, false, true, true,
                             true, false, NULL);
       dotnet_present = (exitstatus == 0);
@@ -382,7 +382,7 @@ execute_csharp_using_dotnet (const char *assembly_path,
 
             /* Open a pipe to the program.  */
             int fd[1];
-            pid_t child = create_pipe_in ("dotnet", "dotnet", argv, NULL,
+            pid_t child = create_pipe_in ("dotnet", "dotnet", argv, NULL, NULL,
                                           DEV_NULL, false, true, false, fd);
             if (child == -1)
               {
@@ -572,7 +572,7 @@ execute_csharp_using_sscli (const char *assembly_path,
 
       argv[0] = "clix";
       argv[1] = NULL;
-      exitstatus = execute ("clix", "clix", argv, NULL,
+      exitstatus = execute ("clix", "clix", argv, NULL, NULL,
                             false, false, true, true,
                             true, false, NULL);
       clix_present = (exitstatus == 0 || exitstatus == 1);
diff --git a/lib/cygpath.c b/lib/cygpath.c
index 7461706089..90caacde42 100644
--- a/lib/cygpath.c
+++ b/lib/cygpath.c
@@ -92,7 +92,7 @@ execute_and_read_line (const char *progname,
   size_t linelen;
 
   /* Open a pipe to the program.  */
-  child = create_pipe_in (progname, prog_path, prog_argv, NULL,
+  child = create_pipe_in (progname, prog_path, prog_argv, NULL, NULL,
                           DEV_NULL, false, true, false, fd);
 
   if (child == -1)
diff --git a/lib/execute.c b/lib/execute.c
index 8bbba2be62..c5ebf29690 100644
--- a/lib/execute.c
+++ b/lib/execute.c
@@ -35,6 +35,7 @@
 #include "fatal-signal.h"
 #include "filename.h"
 #include "findprog.h"
+#include "windows-path.h"
 #include "wait-process.h"
 #include "xalloc.h"
 #include "gettext.h"
@@ -113,6 +114,7 @@ nonintr_open (const char *pathname, int oflag, mode_t mode)
 int
 execute (const char *progname,
          const char *prog_path, const char * const *prog_argv,
+         const char * const *dll_dirs,
          const char *directory,
          bool ignore_sigpipe,
          bool null_stdin, bool null_stdout, bool null_stderr,
@@ -203,7 +205,8 @@ execute (const char *progname,
         (HANDLE) _get_osfhandle (null_stderr ? nulloutfd : STDERR_FILENO);
 
       exitcode = spawnpvech (P_WAIT, prog_path, argv + 1,
-                             (const char * const *) environ, directory,
+                             (const char * const *) environ, dll_dirs,
+                             directory,
                              stdin_handle, stdout_handle, stderr_handle);
 # if 0 /* Executing arbitrary files as shell scripts is unsecure.  */
       if (exitcode == -1 && errno == ENOEXEC)
@@ -213,7 +216,8 @@ execute (const char *progname,
              a hidden element "sh.exe" to argv.  */
           argv[1] = prog_path;
           exitcode = spawnpvech (P_WAIT, argv[0], argv,
-                                 (const char * const *) environ, directory,
+                                 (const char * const *) environ, dll_dirs,
+                                 directory,
                                  stdin_handle, stdout_handle, stderr_handle);
         }
 # endif
@@ -254,6 +258,8 @@ execute (const char *progname,
      subprocess to exit with return code 127.  It is implementation
      dependent which error is reported which way.  We treat both cases as
      equivalent.  */
+  char **child_environ;
+  char **malloced_environ;
   sigset_t blocked_signals;
   posix_spawn_file_actions_t actions;
   bool actions_allocated;
@@ -262,6 +268,18 @@ execute (const char *progname,
   int err;
   pid_t child;
 
+  child_environ = environ;
+  malloced_environ = NULL;
+# if defined _WIN32 || defined __CYGWIN__
+  if (dll_dirs != NULL && dll_dirs[0] != NULL)
+    {
+      malloced_environ = extended_environ (dll_dirs);
+      if (malloced_environ == NULL)
+        goto fail_with_errno;
+      child_environ = malloced_environ;
+    }
+# endif
+
   if (slave_process)
     {
       sigprocmask (SIG_SETMASK, NULL, &blocked_signals);
@@ -300,16 +318,18 @@ execute (const char *progname,
                                                          &blocked_signals))
                       != 0
                       || (err = posix_spawnattr_setflags (&attrs,
-                                                        POSIX_SPAWN_SETSIGMASK))
+                                                          POSIX_SPAWN_SETSIGMASK))
                          != 0)))
 # endif
           || (err = (directory != NULL
                      ? posix_spawn (&child, prog_path, &actions,
                                     attrs_allocated ? &attrs : NULL,
-                                    (char * const *) prog_argv, environ)
+                                    (char * const *) prog_argv,
+                                    child_environ)
                      : posix_spawnp (&child, prog_path, &actions,
                                      attrs_allocated ? &attrs : NULL,
-                                     (char * const *) prog_argv, environ)))
+                                     (char * const *) prog_argv,
+                                     child_environ)))
              != 0))
     {
       if (actions_allocated)
@@ -318,6 +338,11 @@ execute (const char *progname,
         posix_spawnattr_destroy (&attrs);
       if (slave_process)
         unblock_fatal_signals ();
+      if (malloced_environ != NULL)
+        {
+          free (malloced_environ[0]);
+          free (malloced_environ);
+        }
       free (prog_path_to_free);
       if (termsigp != NULL)
         *termsigp = 0;
@@ -332,6 +357,11 @@ execute (const char *progname,
       register_slave_subprocess (child);
       unblock_fatal_signals ();
     }
+  if (malloced_environ != NULL)
+    {
+      free (malloced_environ[0]);
+      free (malloced_environ);
+    }
   free (prog_path_to_free);
 
   return wait_subprocess (child, progname, ignore_sigpipe, null_stderr,
diff --git a/lib/execute.h b/lib/execute.h
index 27ac081f5e..cb43f21e16 100644
--- a/lib/execute.h
+++ b/lib/execute.h
@@ -27,6 +27,7 @@ extern "C" {
    descriptors to /dev/null.  Return its exit code.
    If it didn't terminate correctly, exit if exit_on_error is true, otherwise
    return 127.
+
    progname is the name of the program to be executed by the subprocess, used
    for error messages.
    prog_path is the file name of the program to be executed by the subprocess.
@@ -35,21 +36,31 @@ extern "C" {
    prog_argv is the array of strings that the subprocess shall receive in
    argv[].  It is a NULL-terminated array.  prog_argv[0] should normally be
    identical to prog_path.
+
+   dll_dirs is, on Windows platforms, a NULL-terminated list of directories
+   that contain DLLs needed to execute the program, or NULL if none is needed.
+   On other platforms, always pass NULL.
+
    If directory is not NULL, the command is executed in that directory.  If
    prog_path is a relative file name, it resolved before changing to that
    directory.  The current directory of the current process remains unchanged.
+
    If ignore_sigpipe is true, consider a subprocess termination due to SIGPIPE
    as equivalent to a success.  This is suitable for processes whose only
    purpose is to write to standard output.
+
    If slave_process is true, the child process will be terminated when its
    creator receives a catchable fatal signal.
+
    If termsigp is not NULL, *termsig will be set to the signal that terminated
    the subprocess (if supported by the platform: not on native Windows
    platforms), otherwise 0.
+
    It is recommended that no signal is blocked or ignored while execute()
    is called.  See spawn-pipe.h for the reason.  */
 extern int execute (const char *progname,
                     const char *prog_path, const char * const *prog_argv,
+                    const char * const *dll_dirs,
                     const char *directory,
                     bool ignore_sigpipe,
                     bool null_stdin, bool null_stdout, bool null_stderr,
diff --git a/lib/javacomp.c b/lib/javacomp.c
index 107f373740..c5b90147ca 100644
--- a/lib/javacomp.c
+++ b/lib/javacomp.c
@@ -243,7 +243,7 @@ compile_using_envjavac (const char *javac,
   argv[1] = "-c";
   argv[2] = command;
   argv[3] = NULL;
-  exitstatus = execute (javac, BOURNE_SHELL, argv, NULL,
+  exitstatus = execute (javac, BOURNE_SHELL, argv, NULL, NULL,
                         false, false, false, null_stderr,
                         true, true, NULL);
   err = (exitstatus != 0);
@@ -315,7 +315,7 @@ compile_using_javac (const char * const *java_sources,
       free (command);
     }
 
-  exitstatus = execute ("javac", "javac", argv, NULL,
+  exitstatus = execute ("javac", "javac", argv, NULL, NULL,
                         false, false, false,
                         null_stderr, true, true, NULL);
   err = (exitstatus != 0);
@@ -343,7 +343,7 @@ execute_and_read_line (const char *progname,
   int exitstatus;
 
   /* Open a pipe to the program.  */
-  child = create_pipe_in (progname, prog_path, prog_argv, NULL,
+  child = create_pipe_in (progname, prog_path, prog_argv, NULL, NULL,
                           DEV_NULL, false, true, false, fd);
 
   if (child == -1)
@@ -762,7 +762,7 @@ is_javac_present (void)
 
       argv[0] = "javac";
       argv[1] = NULL;
-      exitstatus = execute ("javac", "javac", argv, NULL,
+      exitstatus = execute ("javac", "javac", argv, NULL, NULL,
                             false, false, true, true,
                             true, false, NULL);
       javac_present = (exitstatus == 0 || exitstatus == 1 || exitstatus == 2);
diff --git a/lib/javaexec.c b/lib/javaexec.c
index 081c54eaaa..7df0a28f76 100644
--- a/lib/javaexec.c
+++ b/lib/javaexec.c
@@ -205,7 +205,7 @@ execute_java_class (const char *class_name,
         argv[0] = "java";
         argv[1] = "-version";
         argv[2] = NULL;
-        exitstatus = execute ("java", "java", argv, NULL,
+        exitstatus = execute ("java", "java", argv, NULL, NULL,
                               false, false, true, true,
                               true, false, NULL);
         java_present = (exitstatus == 0);
@@ -261,7 +261,7 @@ execute_java_class (const char *class_name,
 
         argv[0] = "jre";
         argv[1] = NULL;
-        exitstatus = execute ("jre", "jre", argv, NULL,
+        exitstatus = execute ("jre", "jre", argv, NULL, NULL,
                               false, false, true, true,
                               true, false, NULL);
         jre_present = (exitstatus == 0 || exitstatus == 1);
diff --git a/lib/javaversion.c b/lib/javaversion.c
index fefdfe560d..fb3ce70751 100644
--- a/lib/javaversion.c
+++ b/lib/javaversion.c
@@ -63,7 +63,7 @@ execute_and_read_line (const char *progname,
   size_t linelen;
 
   /* Open a pipe to the JVM.  */
-  child = create_pipe_in (progname, prog_path, prog_argv, NULL,
+  child = create_pipe_in (progname, prog_path, prog_argv, NULL, NULL,
                           DEV_NULL, false, true, false, fd);
 
   if (child == -1)
diff --git a/lib/pipe-filter-gi.c b/lib/pipe-filter-gi.c
index 6bbe4b4ff9..ab26fc4774 100644
--- a/lib/pipe-filter-gi.c
+++ b/lib/pipe-filter-gi.c
@@ -496,7 +496,7 @@ pipe_filter_gi_create (const char *progname,
     (struct pipe_filter_gi *) xmalloc (sizeof (struct pipe_filter_gi));
 
   /* Open a bidirectional pipe to a subprocess.  */
-  filter->child = create_pipe_bidi (progname, prog_path, prog_argv,
+  filter->child = create_pipe_bidi (progname, prog_path, prog_argv, NULL,
                                     NULL, null_stderr, true, exit_on_error,
                                     filter->fd);
   filter->progname = progname;
diff --git a/lib/pipe-filter-ii.c b/lib/pipe-filter-ii.c
index 1786ba267f..0084528898 100644
--- a/lib/pipe-filter-ii.c
+++ b/lib/pipe-filter-ii.c
@@ -269,7 +269,7 @@ pipe_filter_ii_execute (const char *progname,
 #endif
 
   /* Open a bidirectional pipe to a subprocess.  */
-  child = create_pipe_bidi (progname, prog_path, prog_argv,
+  child = create_pipe_bidi (progname, prog_path, prog_argv, NULL,
                             NULL, null_stderr, true, exit_on_error,
                             fd);
   if (child == -1)
diff --git a/lib/spawn-pipe.c b/lib/spawn-pipe.c
index 28e15a117a..fe60646c43 100644
--- a/lib/spawn-pipe.c
+++ b/lib/spawn-pipe.c
@@ -37,6 +37,7 @@
 #include "fatal-signal.h"
 #include "filename.h"
 #include "findprog.h"
+#include "windows-path.h"
 #include "unistd-safer.h"
 #include "wait-process.h"
 #include "xalloc.h"
@@ -138,6 +139,7 @@ static pid_t
 create_pipe (const char *progname,
              const char *prog_path,
              const char * const *prog_argv,
+             const char * const *dll_dirs,
              const char *directory,
              bool pipe_stdin, bool pipe_stdout,
              const char *prog_stdin, const char *prog_stdout,
@@ -313,7 +315,8 @@ create_pipe (const char *progname,
         (HANDLE) _get_osfhandle (null_stderr ? nulloutfd : STDERR_FILENO);
 
       child = spawnpvech (P_NOWAIT, prog_path, argv + 1,
-                          (const char * const *) environ, directory,
+                          (const char * const *) environ, dll_dirs,
+                          directory,
                           stdin_handle, stdout_handle, stderr_handle);
 #  if 0 /* Executing arbitrary files as shell scripts is unsecure.  */
       if (child == -1 && errno == ENOEXEC)
@@ -323,7 +326,8 @@ create_pipe (const char *progname,
              a hidden element "sh.exe" to argv.  */
           argv[1] = prog_path;
           child = spawnpvech (P_NOWAIT, argv[0], argv,
-                              (const char * const *) environ, directory,
+                              (const char * const *) environ, dll_dirs,
+                              directory,
                               stdin_handle, stdout_handle, stderr_handle);
         }
 #  endif
@@ -433,6 +437,8 @@ create_pipe (const char *progname,
 #else
 
   /* Unix API.  */
+  char **child_environ;
+  char **malloced_environ;
   sigset_t blocked_signals;
   posix_spawn_file_actions_t actions;
   bool actions_allocated;
@@ -441,6 +447,18 @@ create_pipe (const char *progname,
   int err;
   pid_t child;
 
+  child_environ = environ;
+  malloced_environ = NULL;
+# if defined _WIN32 || defined __CYGWIN__
+  if (dll_dirs != NULL && dll_dirs[0] != NULL)
+    {
+      malloced_environ = extended_environ (dll_dirs);
+      if (malloced_environ == NULL)
+        goto fail_with_errno;
+      child_environ = malloced_environ;
+    }
+# endif
+
   if (slave_process)
     {
       sigprocmask (SIG_SETMASK, NULL, &blocked_signals);
@@ -499,24 +517,26 @@ create_pipe (const char *progname,
 # if defined _WIN32 && !defined __CYGWIN__
                       (err = posix_spawnattr_setpgroup (&attrs, 0)) != 0
                       || (err = posix_spawnattr_setflags (&attrs,
-                                                         POSIX_SPAWN_SETPGROUP))
+                                                          POSIX_SPAWN_SETPGROUP))
                          != 0
 # else
                       (err = posix_spawnattr_setsigmask (&attrs,
                                                          &blocked_signals))
                       != 0
                       || (err = posix_spawnattr_setflags (&attrs,
-                                                        POSIX_SPAWN_SETSIGMASK))
+                                                          POSIX_SPAWN_SETSIGMASK))
                          != 0
 # endif
              )   )   )
           || (err = (directory != NULL
                      ? posix_spawn (&child, prog_path, &actions,
                                     attrs_allocated ? &attrs : NULL,
-                                    (char * const *) prog_argv, environ)
+                                    (char * const *) prog_argv,
+                                    child_environ)
                      : posix_spawnp (&child, prog_path, &actions,
                                      attrs_allocated ? &attrs : NULL,
-                                     (char * const *) prog_argv, environ)))
+                                     (char * const *) prog_argv,
+                                     child_environ)))
              != 0))
     {
       if (actions_allocated)
@@ -525,6 +545,11 @@ create_pipe (const char *progname,
         posix_spawnattr_destroy (&attrs);
       if (slave_process)
         unblock_fatal_signals ();
+      if (malloced_environ != NULL)
+        {
+          free (malloced_environ[0]);
+          free (malloced_environ);
+        }
       if (pipe_stdout)
         {
           close (ifd[0]);
@@ -547,6 +572,11 @@ create_pipe (const char *progname,
       register_slave_subprocess (child);
       unblock_fatal_signals ();
     }
+  if (malloced_environ != NULL)
+    {
+      free (malloced_environ[0]);
+      free (malloced_environ);
+    }
   if (pipe_stdin)
     close (ofd[0]);
   if (pipe_stdout)
@@ -582,12 +612,14 @@ create_pipe (const char *progname,
 pid_t
 create_pipe_bidi (const char *progname,
                   const char *prog_path, const char * const *prog_argv,
+                  const char * const *dll_dirs,
                   const char *directory,
                   bool null_stderr,
                   bool slave_process, bool exit_on_error,
                   int fd[2])
 {
-  pid_t result = create_pipe (progname, prog_path, prog_argv, directory,
+  pid_t result = create_pipe (progname, prog_path, prog_argv, dll_dirs,
+                              directory,
                               true, true, NULL, NULL,
                               null_stderr, slave_process, exit_on_error,
                               fd);
@@ -604,13 +636,15 @@ create_pipe_bidi (const char *progname,
 pid_t
 create_pipe_in (const char *progname,
                 const char *prog_path, const char * const *prog_argv,
+                const char * const *dll_dirs,
                 const char *directory,
                 const char *prog_stdin, bool null_stderr,
                 bool slave_process, bool exit_on_error,
                 int fd[1])
 {
   int iofd[2];
-  pid_t result = create_pipe (progname, prog_path, prog_argv, directory,
+  pid_t result = create_pipe (progname, prog_path, prog_argv, dll_dirs,
+                              directory,
                               false, true, prog_stdin, NULL,
                               null_stderr, slave_process, exit_on_error,
                               iofd);
@@ -629,13 +663,15 @@ create_pipe_in (const char *progname,
 pid_t
 create_pipe_out (const char *progname,
                  const char *prog_path, const char * const *prog_argv,
+                 const char * const *dll_dirs,
                  const char *directory,
                  const char *prog_stdout, bool null_stderr,
                  bool slave_process, bool exit_on_error,
                  int fd[1])
 {
   int iofd[2];
-  pid_t result = create_pipe (progname, prog_path, prog_argv, directory,
+  pid_t result = create_pipe (progname, prog_path, prog_argv, dll_dirs,
+                              directory,
                               true, false, NULL, prog_stdout,
                               null_stderr, slave_process, exit_on_error,
                               iofd);
diff --git a/lib/spawn-pipe.h b/lib/spawn-pipe.h
index 9f558ddf18..1fe07da120 100644
--- a/lib/spawn-pipe.h
+++ b/lib/spawn-pipe.h
@@ -49,6 +49,10 @@ extern "C" {
    argv[].  It is a NULL-terminated array.  prog_argv[0] should normally be
    identical to prog_path.
 
+   dll_dirs is, on Windows platforms, a NULL-terminated list of directories
+   that contain DLLs needed to execute the program, or NULL if none is needed.
+   On other platforms, always pass NULL.
+
    If directory is not NULL, the subprocess is started in that directory.  If
    prog_path is a relative file name, it resolved before changing to that
    directory.  The current directory of the current process remains unchanged.
@@ -96,6 +100,7 @@ extern "C" {
 extern pid_t create_pipe_out (const char *progname,
                               const char *prog_path,
                               const char * const *prog_argv,
+                              const char * const *dll_dirs,
                               const char *directory,
                               const char *prog_stdout, bool null_stderr,
                               bool slave_process, bool exit_on_error,
@@ -111,6 +116,7 @@ extern pid_t create_pipe_out (const char *progname,
 extern pid_t create_pipe_in (const char *progname,
                              const char *prog_path,
                              const char * const *prog_argv,
+                             const char * const *dll_dirs,
                              const char *directory,
                              const char *prog_stdin, bool null_stderr,
                              bool slave_process, bool exit_on_error,
@@ -141,6 +147,7 @@ extern pid_t create_pipe_in (const char *progname,
 extern pid_t create_pipe_bidi (const char *progname,
                                const char *prog_path,
                                const char * const *prog_argv,
+                               const char * const *dll_dirs,
                                const char *directory,
                                bool null_stderr,
                                bool slave_process, bool exit_on_error,
diff --git a/lib/spawni.c b/lib/spawni.c
index 8ddf9c5999..211394bb9f 100644
--- a/lib/spawni.c
+++ b/lib/spawni.c
@@ -665,7 +665,7 @@ __spawni (pid_t *pid, const char *prog_filename,
     envblock = NULL;
   else
     {
-      envblock = compose_envblock (envp);
+      envblock = compose_envblock (envp, NULL);
       if (envblock == NULL)
         {
           free (command);
diff --git a/lib/windows-path.c b/lib/windows-path.c
new file mode 100644
index 0000000000..d238653fe1
--- /dev/null
+++ b/lib/windows-path.c
@@ -0,0 +1,146 @@
+/* Auxiliary functions for the creation of subprocesses on Windows.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   Written by Bruno Haible <br...@clisp.org>, 2024.
+
+   This file is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
+   License, or (at your option) any later version.
+
+   This file is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "windows-path.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+char *
+extended_PATH (const char * const *dll_dirs)
+{
+  /* Create a string PATH=(dll_dirs[0];dll_dirs[1];...;)old_PATH  */
+  const char *old_PATH = getenv ("PATH");
+  if (old_PATH == NULL)
+    old_PATH = "";
+  size_t size;
+  {
+    size = 5;
+    {
+      size_t i;
+      for (i = 0; dll_dirs[i] != NULL; i++)
+        size += strlen (dll_dirs[i]) + 1;
+    }
+    size += strlen (old_PATH) + 1;
+  }
+  char *new_PATH = (char *) malloc (size);
+  if (new_PATH == NULL)
+    return NULL;
+  {
+    char *p = new_PATH;
+    {
+      memcpy (p, "PATH=", 5);
+      p += 5;
+    }
+    {
+      size_t i;
+      for (i = 0; dll_dirs[i] != NULL; i++)
+        {
+          size_t l = strlen (dll_dirs[i]);
+          memcpy (p, dll_dirs[i], l);
+          p += l;
+#if defined _WIN32 && !defined __CYGWIN__
+          *p++ = ';';
+#else
+          *p++ = ':';
+#endif
+        }
+    }
+    {
+      size_t l = strlen (old_PATH);
+      memcpy (p, old_PATH, l);
+      p += l;
+      *p = '\0';
+    }
+  }
+  return new_PATH;
+}
+
+char **
+extended_environ (const char * const *dll_dirs)
+{
+  char *child_PATH = extended_PATH (dll_dirs);
+  if (child_PATH == NULL)
+    return NULL;
+
+  /* Create a shallow copy of environ, adding the child_PATH and removing
+     the original "PATH=..." string.
+     This is a bit hairy, because we don't have a lock that would prevent
+     other threads from making modifications in ENVP.  So, just make sure
+     we don't crash; but if other threads are making modifications, part
+     of the result may be wrong.  */
+  char **envp;
+
+#if defined _WIN32 && !defined __CYGWIN__
+  envp = _environ;
+#else
+  envp = environ;
+#endif
+
+ retry:
+  {
+    /* Guess the size of the needed block of memory.
+       The guess will be exact if other threads don't make modifications.  */
+    size_t size = 0;
+    {
+      char **ep;
+      char *p;
+      for (ep = envp; (p = *ep) != NULL; ep++)
+        if (strncmp (p, "PATH=", 5) != 0)
+          size += 1;
+    }
+    char **new_environ = (char **) malloc ((1 + size + 1) * sizeof (char *));
+    if (new_environ == NULL)
+      {
+        free (child_PATH);
+        errno = ENOMEM;
+        return NULL;
+      }
+    char **nep = new_environ;
+    *nep++ = child_PATH;
+    {
+      size_t i = 0;
+      char **ep;
+      char *p;
+      for (ep = envp; (p = *ep) != NULL; ep++)
+        if (strncmp (p, "PATH=", 5) != 0)
+          {
+            if (i == size)
+              {
+                /* Other threads did modifications.  Restart.  */
+                free (new_environ);
+                goto retry;
+              }
+            *nep++ = p;
+            i += 1;
+          }
+      if (i < size)
+        {
+          /* Other threads did modifications.  Restart.  */
+          free (new_environ);
+          goto retry;
+        }
+    }
+    *nep = NULL;
+    return new_environ;
+  }
+}
diff --git a/lib/windows-path.h b/lib/windows-path.h
new file mode 100644
index 0000000000..f4199db697
--- /dev/null
+++ b/lib/windows-path.h
@@ -0,0 +1,54 @@
+/* Auxiliary functions for the creation of subprocesses on Windows.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   Written by Bruno Haible <br...@clisp.org>, 2024.
+
+   This file is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
+   License, or (at your option) any later version.
+
+   This file is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#ifndef _WINDOWS_PATH_H
+#define _WINDOWS_PATH_H
+
+/* This file uses _GL_ATTRIBUTE_MALLOC.  */
+#if !_GL_CONFIG_H_INCLUDED
+ #error "Please include config.h first."
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Returns an augmented "PATH=..." string for the environment of a child process.
+   dll_dirs is a NULL-terminated list of directories that contain DLLs needed to
+   execute the program, or NULL if none is needed.
+   Returns a freshly allocated string.  In case of memory allocation failure,
+   NULL is returned, with errno set.  */
+extern char * extended_PATH (const char * const *dll_dirs)
+  _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_DEALLOC_FREE;
+
+/* Returns an augmented environment for a child process.
+   dll_dirs is a NULL-terminated list of directories that contain DLLs needed to
+   execute the program, or NULL if none is needed.
+   Returns a freshly allocated string array, with a freshly allocated first
+   string.  In case of memory allocation failure, NULL is returned, with errno
+   set.  */
+extern char ** extended_environ (const char * const *dll_dirs)
+  _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_DEALLOC_FREE;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _WINDOWS_PATH_H */
diff --git a/lib/windows-spawn.c b/lib/windows-spawn.c
index e2222f4fc8..ac098b4262 100644
--- a/lib/windows-spawn.c
+++ b/lib/windows-spawn.c
@@ -38,6 +38,7 @@
 #include <process.h>
 
 #include "findprog.h"
+#include "windows-path.h"
 
 /* Don't assume that UNICODE is not defined.  */
 #undef STARTUPINFO
@@ -244,7 +245,7 @@ compose_command (const char * const *argv)
 }
 
 char *
-compose_envblock (const char * const *envp)
+compose_envblock (const char * const *envp, const char *new_PATH)
 {
   /* This is a bit hairy, because we don't have a lock that would prevent other
      threads from making modifications in ENVP.  So, just make sure we don't
@@ -257,8 +258,11 @@ compose_envblock (const char * const *envp)
     size_t total_size = 0;
     const char * const *ep;
     const char *p;
+    if (new_PATH != NULL)
+      total_size += strlen (new_PATH) + 1;
     for (ep = envp; (p = *ep) != NULL; ep++)
-      total_size += strlen (p) + 1;
+      if (!(new_PATH != NULL && strncmp (p, "PATH=", 5) == 0))
+        total_size += strlen (p) + 1;
     size_t envblock_size = total_size;
 
     /* Allocate the block of memory.  */
@@ -269,34 +273,42 @@ compose_envblock (const char * const *envp)
         return NULL;
       }
     size_t envblock_used = 0;
-    for (ep = envp; (p = *ep) != NULL; ep++)
+    if (new_PATH != NULL)
       {
-        size_t size = strlen (p) + 1;
-        if (envblock_used + size > envblock_size)
-          {
-            /* Other threads did modifications.  Need more memory.  */
-            envblock_size += envblock_size / 2;
-            if (envblock_used + size > envblock_size)
-              envblock_size = envblock_used + size;
-
-            char *new_envblock = (char *) realloc (envblock, envblock_size + 1);
-            if (new_envblock == NULL)
-              {
-                free (envblock);
-                errno = ENOMEM;
-                return NULL;
-              }
-            envblock = new_envblock;
-          }
-        memcpy (envblock + envblock_used, p, size);
+        size_t size = strlen (new_PATH) + 1;
+        memcpy (envblock + envblock_used, new_PATH, size);
         envblock_used += size;
-        if (envblock[envblock_used - 1] != '\0')
-          {
-            /* Other threads did modifications.  Restart.  */
-            free (envblock);
-            goto retry;
-          }
       }
+    for (ep = envp; (p = *ep) != NULL; ep++)
+      if (!(new_PATH != NULL && strncmp (p, "PATH=", 5) == 0))
+        {
+          size_t size = strlen (p) + 1;
+          if (envblock_used + size > envblock_size)
+            {
+              /* Other threads did modifications.  Need more memory.  */
+              envblock_size += envblock_size / 2;
+              if (envblock_used + size > envblock_size)
+                envblock_size = envblock_used + size;
+
+              char *new_envblock =
+                (char *) realloc (envblock, envblock_size + 1);
+              if (new_envblock == NULL)
+                {
+                  free (envblock);
+                  errno = ENOMEM;
+                  return NULL;
+                }
+              envblock = new_envblock;
+            }
+          memcpy (envblock + envblock_used, p, size);
+          envblock_used += size;
+          if (envblock[envblock_used - 1] != '\0')
+            {
+              /* Other threads did modifications.  Restart.  */
+              free (envblock);
+              goto retry;
+            }
+        }
     envblock[envblock_used] = '\0';
     return envblock;
   }
@@ -573,6 +585,7 @@ intptr_t
 spawnpvech (int mode,
             const char *progname, const char * const *argv,
             const char * const *envp,
+            const char * const *dll_dirs,
             const char *currdir,
             HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle)
 {
@@ -600,11 +613,23 @@ spawnpvech (int mode,
 
   /* Copy *ENVP into a contiguous block of memory.  */
   char *envblock;
-  if (envp == NULL)
+  if (envp == NULL && !(dll_dirs != NULL && dll_dirs[0] != NULL))
     envblock = NULL;
   else
     {
-      envblock = compose_envblock (envp);
+      if (envp == NULL)
+        /* Documentation:
+           <https://learn.microsoft.com/en-us/cpp/c-runtime-library/environ-wenviron>  */
+        envp = (const char **) _environ;
+      char *new_PATH = NULL;
+      if (dll_dirs != NULL && dll_dirs[0] != NULL)
+        {
+          new_PATH = extended_PATH (dll_dirs);
+          if (new_PATH == NULL)
+            goto out_of_memory_2;
+        }
+      envblock = compose_envblock (envp, new_PATH);
+      free (new_PATH);
       if (envblock == NULL)
         goto out_of_memory_2;
     }
diff --git a/lib/windows-spawn.h b/lib/windows-spawn.h
index 9f18ab2a64..3a33016a5c 100644
--- a/lib/windows-spawn.h
+++ b/lib/windows-spawn.h
@@ -88,9 +88,11 @@ extern char * compose_command (const char * const *argv)
 /* Composes the block of memory that contains the environment variables.
    ENVP must contain an environment (a NULL-terminated array of string of the
    form VARIABLE=VALUE).
+   NEW_PATH must be a string of the form PATH=VALUE, or NULL if the PATH
+   environment variable from this process is suitable for the child process.
    Returns a freshly allocated block of memory.  In case of memory allocation
    failure, NULL is returned, with errno set.  */
-extern char * compose_envblock (const char * const *envp)
+extern char * compose_envblock (const char * const *envp, const char *new_PATH)
   _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_DEALLOC_FREE;
 
 
@@ -176,6 +178,9 @@ extern int convert_CreateProcess_error (DWORD error);
    ENVP is the NULL-terminated set of environment variable assignments, or NULL
    to inherit the initial environ variable assignments from the caller and
    ignore all calls to putenv(), setenv(), unsetenv() done in the caller.
+   DLL_DIRS is, on Windows platforms, a NULL-terminated list of directories
+   that contain DLLs needed to execute the program, or NULL if none is needed.
+   On other platforms, always pass NULL.
    CURRDIR is the directory in which to start the program, or NULL to inherit
    the working directory from the caller.
    STDIN_HANDLE, STDOUT_HANDLE, STDERR_HANDLE are the handles to use for the
@@ -188,6 +193,7 @@ extern int convert_CreateProcess_error (DWORD error);
 extern intptr_t spawnpvech (int mode,
                             const char *progname, const char * const *argv,
                             const char * const *envp,
+                            const char * const *dll_dirs,
                             const char *currdir,
                             HANDLE stdin_handle, HANDLE stdout_handle,
                             HANDLE stderr_handle);
diff --git a/modules/windows-spawn b/modules/windows-spawn
index 2b0c7ccf3c..29b89ec244 100644
--- a/modules/windows-spawn
+++ b/modules/windows-spawn
@@ -1,11 +1,14 @@
 Description:
-Auxiliary functions for the creation of subprocesses on native Windows.
+Auxiliary functions for the creation of subprocesses on Windows.
 
 Files:
+lib/windows-path.h
+lib/windows-path.c
 lib/windows-spawn.h
 lib/windows-spawn.c
 
 Depends-on:
+environ
 findprog-in
 msvc-nothrow
 stdbool
@@ -17,15 +20,21 @@ malloc-posix
 
 configure.ac:
 AC_REQUIRE([AC_CANONICAL_HOST])
+gl_CONDITIONAL([GL_COND_OBJ_WINDOWS_PATH],
+               [case "$host_os" in cygwin* | mingw* | windows*) true;; *) false;; esac])
 gl_CONDITIONAL([GL_COND_OBJ_WINDOWS_SPAWN],
                [case "$host_os" in mingw* | windows*) true;; *) false;; esac])
 
 Makefile.am:
+if GL_COND_OBJ_WINDOWS_PATH
+lib_SOURCES += windows-path.c
+endif
 if GL_COND_OBJ_WINDOWS_SPAWN
 lib_SOURCES += windows-spawn.c
 endif
 
 Include:
+"windows-path.h"
 "windows-spawn.h"
 
 License:
diff --git a/tests/test-execute-main.c b/tests/test-execute-main.c
index 4703941bea..307b1f7cbf 100644
--- a/tests/test-execute-main.c
+++ b/tests/test-execute-main.c
@@ -104,7 +104,7 @@ main (int argc, char *argv[])
       {
         /* Check an invocation without arguments.  Check the exit code.  */
         const char *prog_argv[2] = { prog_path, NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 40);
       }
@@ -113,7 +113,7 @@ main (int argc, char *argv[])
       {
         /* Check an invocation of a nonexistent program.  */
         const char *prog_argv[3] = { "./nonexistent", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 127);
       }
@@ -137,7 +137,7 @@ main (int argc, char *argv[])
             "",
             NULL
           };
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
       }
@@ -148,7 +148,7 @@ main (int argc, char *argv[])
         /* Check SIGPIPE handling with ignore_sigpipe = false.  */
         const char *prog_argv[3] = { prog_path, "3", NULL };
         int termsig = 0x7DEADBEE;
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            false, false, false, false, true, false, &termsig);
         ASSERT (ret == 127);
         ASSERT (termsig == SIGPIPE);
@@ -161,7 +161,7 @@ main (int argc, char *argv[])
         /* Check SIGPIPE handling with ignore_sigpipe = true.  */
         const char *prog_argv[3] = { prog_path, "4", NULL };
         int termsig = 0x7DEADBEE;
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            true, false, false, false, true, false, &termsig);
         ASSERT (ret == 0);
         ASSERT (termsig == SIGPIPE);
@@ -173,7 +173,7 @@ main (int argc, char *argv[])
         /* Check other signal.  */
         const char *prog_argv[3] = { prog_path, "5", NULL };
         int termsig = 0x7DEADBEE;
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            false, false, false, false, true, false, &termsig);
         ASSERT (ret == 127);
         #if defined _WIN32 && !defined __CYGWIN__
@@ -194,7 +194,7 @@ main (int argc, char *argv[])
         ASSERT (fp != NULL);
 
         const char *prog_argv[3] = { prog_path, "6", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
 
@@ -213,7 +213,7 @@ main (int argc, char *argv[])
         ASSERT (fp != NULL);
 
         const char *prog_argv[3] = { prog_path, "7", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            false, true, false, false, true, false, NULL);
         ASSERT (ret == 0);
 
@@ -228,7 +228,7 @@ main (int argc, char *argv[])
         ASSERT (fp != NULL);
 
         const char *prog_argv[3] = { prog_path, "8", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
 
@@ -248,7 +248,7 @@ main (int argc, char *argv[])
         ASSERT (fp != NULL);
 
         const char *prog_argv[3] = { prog_path, "9", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
       }
@@ -260,7 +260,7 @@ main (int argc, char *argv[])
         ASSERT (fp != NULL);
 
         const char *prog_argv[3] = { prog_path, "10", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            false, false, true, false, true, false, NULL);
         ASSERT (ret == 0);
 
@@ -280,7 +280,7 @@ main (int argc, char *argv[])
         ASSERT (fp != NULL);
 
         const char *prog_argv[3] = { prog_path, "11", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
 
@@ -300,7 +300,7 @@ main (int argc, char *argv[])
         ASSERT (fp != NULL);
 
         const char *prog_argv[3] = { prog_path, "12", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
       }
@@ -312,7 +312,7 @@ main (int argc, char *argv[])
         ASSERT (fp != NULL);
 
         const char *prog_argv[3] = { prog_path, "13", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            false, false, false, true, true, false, NULL);
         ASSERT (ret == 0);
 
@@ -330,7 +330,7 @@ main (int argc, char *argv[])
         /* Check file descriptors >= 3 can be inherited.  */
         ASSERT (dup2 (STDOUT_FILENO, 10) >= 0);
         const char *prog_argv[3] = { prog_path, "14", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            true, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
       }
@@ -340,7 +340,7 @@ main (int argc, char *argv[])
         /* Check file descriptors >= 3 can be inherited.  */
         ASSERT (fcntl (STDOUT_FILENO, F_DUPFD, 10) >= 0);
         const char *prog_argv[3] = { prog_path, "15", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            true, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
       }
@@ -350,7 +350,7 @@ main (int argc, char *argv[])
         /* Check file descriptors >= 3 with O_CLOEXEC bit are not inherited.  */
         ASSERT (fcntl (STDOUT_FILENO, F_DUPFD_CLOEXEC, 10) >= 0);
         const char *prog_argv[3] = { prog_path, "16", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            true, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
       }
@@ -375,7 +375,7 @@ main (int argc, char *argv[])
         /* The file position is now 2.  */
 
         const char *prog_argv[3] = { prog_path, "17", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
 
@@ -399,7 +399,7 @@ main (int argc, char *argv[])
         /* The file position is now 3.  */
 
         const char *prog_argv[3] = { prog_path, "18", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
 
@@ -435,7 +435,7 @@ main (int argc, char *argv[])
         fd_out = 16;
 
         const char *prog_argv[3] = { prog_path, "19", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            false, false, false, false, true, false, NULL);
         #if defined _WIN32 && ! defined __CYGWIN__
         ASSERT (ret == 4 + 2 * (_isatty (15) != 0) + (_isatty (16) != 0));
@@ -459,7 +459,7 @@ main (int argc, char *argv[])
         int fd_out = 16;
 
         const char *prog_argv[3] = { prog_path, "20", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            false, false, false, false, true, false, NULL);
         #if defined _WIN32 && ! defined __CYGWIN__
         ASSERT (ret == 4 + 2 * (_isatty (15) != 0) + (_isatty (16) != 0));
@@ -485,7 +485,7 @@ main (int argc, char *argv[])
         #endif
 
         const char *prog_argv[4] = { prog_path, "21", cwd, NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv, BASE ".sub",
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, BASE ".sub",
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
 
diff --git a/tests/test-execute-script.c b/tests/test-execute-script.c
index dfb9d8c259..a5a271d838 100644
--- a/tests/test-execute-script.c
+++ b/tests/test-execute-script.c
@@ -57,7 +57,7 @@ main ()
           (i == 0 ? SRCDIR "executable-script" : SRCDIR "executable-script.sh");
         const char *prog_argv[2] = { prog_path, NULL };
 
-        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 127);
       }
@@ -78,7 +78,7 @@ main ()
     const char *prog_path = SRCDIR "executable-shell-script";
     const char *prog_argv[2] = { prog_path, NULL };
 
-    int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+    int ret = execute (progname, prog_argv[0], prog_argv, NULL, NULL,
                        false, false, false, false, true, false, NULL);
     ASSERT (ret == 0);
 
diff --git a/tests/test-spawn-pipe-main.c b/tests/test-spawn-pipe-main.c
index 612d47bed5..98998ea9b8 100644
--- a/tests/test-spawn-pipe-main.c
+++ b/tests/test-spawn-pipe-main.c
@@ -51,7 +51,7 @@ test_pipe (const char *prog, bool stderr_closed)
   argv[0] = prog;
   argv[1] = (stderr_closed ? "1" : "0");
   argv[2] = NULL;
-  pid = create_pipe_bidi (prog, prog, argv, NULL, false, true, true, fd);
+  pid = create_pipe_bidi (prog, prog, argv, NULL, NULL, false, true, true, fd);
   ASSERT (0 <= pid);
   ASSERT (STDERR_FILENO < fd[0]);
   ASSERT (STDERR_FILENO < fd[1]);
diff --git a/tests/test-spawn-pipe-script.c b/tests/test-spawn-pipe-script.c
index 07b06c1b29..ed43c2f18a 100644
--- a/tests/test-spawn-pipe-script.c
+++ b/tests/test-spawn-pipe-script.c
@@ -50,7 +50,7 @@ main ()
           (i == 0 ? SRCDIR "executable-script" : SRCDIR "executable-script.sh");
         const char *prog_argv[2] = { prog_path, NULL };
 
-        pid = create_pipe_in (progname, prog_argv[0], prog_argv, NULL,
+        pid = create_pipe_in (progname, prog_argv[0], prog_argv, NULL, NULL,
                               NULL, false, true, false, fd);
         if (pid >= 0)
           {
@@ -80,7 +80,7 @@ main ()
     const char *prog_path = SRCDIR "executable-shell-script";
     const char *prog_argv[2] = { prog_path, NULL };
 
-    pid = create_pipe_in (progname, prog_argv[0], prog_argv, NULL,
+    pid = create_pipe_in (progname, prog_argv[0], prog_argv, NULL, NULL,
                           NULL, false, true, false, fd);
     ASSERT (pid >= 0);
     ASSERT (fd[0] > STDERR_FILENO);
-- 
2.34.1

Reply via email to