]> Savannah Git Hosting - gnulib.git/commitdiff
execute: Allow caller to specify directory for the subprocess.
authorBruno Haible <bruno@clisp.org>
Wed, 2 Dec 2020 16:44:04 +0000 (17:44 +0100)
committerBruno Haible <bruno@clisp.org>
Wed, 2 Dec 2020 16:44:58 +0000 (17:44 +0100)
* lib/execute.h (execute): Add directory argument.
* lib/execute.c: Include canonicalize.h, filename.h, findprog.h.
(execute): Add directory argument. If specified, resolve the program
file name and make it absolute, first. Pass the directory to spawnpvech
and posix_spawn_file_actions_addchdir.
* modules/execute (Depends-on): Add canonicalize, filename, findprog-in,
posix_spawn, posix_spawn_file_actions_addchdir.
* tests/test-execute-main.c: Add test for passing a directory.
* tests/test-execute-child.c: Likewise.
* tests/test-execute.sh: Update.
* modules/execute-tests (Depends-on): Add mkdir.
* NEWS: Mention the change.
* lib/csharpcomp.c (compile_csharp_using_sscli): Update.
* lib/csharpexec.c (execute_csharp_using_mono,
execute_csharp_using_sscli): Update.
* lib/javacomp.c (compile_using_envjavac, compile_using_gcj,
compile_using_javac, compile_using_jikes, is_javac_present,
is_jikes_present): Update.
* lib/javaexec.c (execute_java_class): Update.

13 files changed:
ChangeLog
NEWS
lib/csharpcomp.c
lib/csharpexec.c
lib/execute.c
lib/execute.h
lib/javacomp.c
lib/javaexec.c
modules/execute
modules/execute-tests
tests/test-execute-child.c
tests/test-execute-main.c
tests/test-execute.sh

index 8e9e032ba7427bed9712b94a518695fac7664638..b5ea083ff5585b91eee2f765cade3d5060dc8d6e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2020-12-02  Bruno Haible  <bruno@clisp.org>
+
+       execute: Allow caller to specify directory for the subprocess.
+       * lib/execute.h (execute): Add directory argument.
+       * lib/execute.c: Include canonicalize.h, filename.h, findprog.h.
+       (execute): Add directory argument. If specified, resolve the program
+       file name and make it absolute, first. Pass the directory to spawnpvech
+       and posix_spawn_file_actions_addchdir.
+       * modules/execute (Depends-on): Add canonicalize, filename, findprog-in,
+       posix_spawn, posix_spawn_file_actions_addchdir.
+       * tests/test-execute-main.c: Add test for passing a directory.
+       * tests/test-execute-child.c: Likewise.
+       * tests/test-execute.sh: Update.
+       * modules/execute-tests (Depends-on): Add mkdir.
+       * NEWS: Mention the change.
+       * lib/csharpcomp.c (compile_csharp_using_sscli): Update.
+       * lib/csharpexec.c (execute_csharp_using_mono,
+       execute_csharp_using_sscli): Update.
+       * lib/javacomp.c (compile_using_envjavac, compile_using_gcj,
+       compile_using_javac, compile_using_jikes, is_javac_present,
+       is_jikes_present): Update.
+       * lib/javaexec.c (execute_java_class): Update.
+
 2020-12-01  Bruno Haible  <bruno@clisp.org>
 
        vma-iter: Add support for macOS11/arm64.
diff --git a/NEWS b/NEWS
index d76e3e77ce0321eda0b1ec215337f7aa02025219..445266d6e864da5d3c4af064e3158b8c321f5152 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -60,6 +60,10 @@ User visible incompatible changes
 
 Date        Modules         Changes
 
+2020-12-02  execute         The function 'execute' now takes a 4th argument
+                            'const char *directory'. To maintain the previous
+                            behaviour, insert NULL as additional 4th argument.
+
 2020-10-16  hash            This module deprecates the 'hash_delete' function
                             using gcc's "deprecated" attribute.  Use the better-
                             named 'hash_remove' equivalent.
index fac9a895d9de41ea4811e25174d773fe0d13e62c..cf68c5a82bbb3b2d6493914b95e7501687d8aea9 100644 (file)
@@ -378,7 +378,8 @@ compile_csharp_using_sscli (const char * const *sources,
           free (command);
         }
 
-      exitstatus = execute ("csc", "csc", argv, false, false, false, false,
+      exitstatus = execute ("csc", "csc", argv, NULL,
+                            false, false, false, false,
                             true, true, NULL);
 
       for (i = 2; i < 3 + libdirs_count + libraries_count; i++)
index b54f14e526bb3ef5bd25e6f612d8b83edce37257..5e2aa2c7530e758e0a91fbd5b178c24c8dfddb92 100644 (file)
@@ -106,7 +106,8 @@ execute_csharp_using_mono (const char *assembly_path,
       argv[0] = "mono";
       argv[1] = "--version";
       argv[2] = NULL;
-      exitstatus = execute ("mono", "mono", argv, false, false, true, true,
+      exitstatus = execute ("mono", "mono", argv, NULL,
+                            false, false, true, true,
                             true, false, NULL);
       mono_present = (exitstatus == 0);
       mono_tested = true;
@@ -167,7 +168,8 @@ execute_csharp_using_sscli (const char *assembly_path,
 
       argv[0] = "clix";
       argv[1] = NULL;
-      exitstatus = execute ("clix", "clix", argv, false, false, true, true,
+      exitstatus = execute ("clix", "clix", argv, NULL,
+                            false, false, true, true,
                             true, false, NULL);
       clix_present = (exitstatus == 0 || exitstatus == 1);
       clix_tested = true;
index 15556cf7915fa7d29093a79b711122072d32693c..03fb6ec2795579336d4d95612f4d1cb1d7ce1d7f 100644 (file)
 #include <signal.h>
 #include <unistd.h>
 
+#include "canonicalize.h"
 #include "error.h"
 #include "fatal-signal.h"
+#include "filename.h"
+#include "findprog.h"
 #include "wait-process.h"
 #include "gettext.h"
 
@@ -94,17 +97,66 @@ nonintr_open (const char *pathname, int oflag, mode_t mode)
 int
 execute (const char *progname,
          const char *prog_path, char **prog_argv,
+         const char *directory,
          bool ignore_sigpipe,
          bool null_stdin, bool null_stdout, bool null_stderr,
          bool slave_process, bool exit_on_error,
          int *termsigp)
 {
+  int saved_errno;
+  char *prog_path_to_free = NULL;
+
+  if (directory != NULL)
+    {
+      /* If a change of directory is requested, make sure PROG_PATH is absolute
+         before we do so.  This is needed because
+           - posix_spawn and posix_spawnp are required to resolve a relative
+             PROG_PATH *after* changing the directory.  See
+             <https://www.austingroupbugs.net/view.php?id=1208>:
+               "if this pathname does not start with a <slash> it shall be
+                interpreted relative to the working directory of the child
+                process _after_ all file_actions have been performed."
+             But this would be a surprising application behaviour, possibly
+             even security relevant.
+           - For the Windows CreateProcess() function, it is unspecified whether
+             a relative file name is interpreted to the parent's current
+             directory or to the specified directory.  See
+             <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa>  */
+      if (! IS_ABSOLUTE_FILE_NAME (prog_path))
+        {
+          const char *resolved_prog =
+            find_in_given_path (prog_path, getenv ("PATH"), false);
+          if (resolved_prog == NULL)
+            goto fail_with_errno;
+          if (resolved_prog != prog_path)
+            prog_path_to_free = (char *) resolved_prog;
+          prog_path = resolved_prog;
+
+          if (! IS_ABSOLUTE_FILE_NAME (prog_path))
+            {
+              char *absolute_prog =
+                canonicalize_filename_mode (prog_path,
+                                            CAN_MISSING | CAN_NOLINKS);
+              if (absolute_prog == NULL)
+                {
+                  saved_errno = errno;
+                  free (prog_path_to_free);
+                  goto fail_with_saved_errno;
+                }
+              free (prog_path_to_free);
+              prog_path_to_free = absolute_prog;
+              prog_path = absolute_prog;
+
+              if (! IS_ABSOLUTE_FILE_NAME (prog_path))
+                abort ();
+            }
+        }
+    }
+
 #if defined _WIN32 && ! defined __CYGWIN__
 
   /* Native Windows API.  */
 
-  int saved_errno;
-
   /* FIXME: Need to free memory allocated by prepare_spawn.  */
   prog_argv = prepare_spawn (prog_argv);
 
@@ -133,16 +185,17 @@ execute (const char *progname,
         (HANDLE) _get_osfhandle (null_stderr ? nulloutfd : STDERR_FILENO);
 
       exitcode = spawnpvech (P_WAIT, prog_path, (const char **) prog_argv,
-                             (const char **) environ, NULL,
+                             (const char **) environ, directory,
                              stdin_handle, stdout_handle, stderr_handle);
       if (exitcode == -1 && errno == ENOEXEC)
         {
           /* prog is not a native executable.  Try to execute it as a
              shell script.  Note that prepare_spawn() has already prepended
              a hidden element "sh.exe" to prog_argv.  */
+          prog_argv[0] = prog_path;
           --prog_argv;
           exitcode = spawnpvech (P_WAIT, prog_argv[0], (const char **) prog_argv,
-                                 (const char **) environ, NULL,
+                                 (const char **) environ, directory,
                                  stdin_handle, stdout_handle, stderr_handle);
         }
     }
@@ -152,17 +205,13 @@ execute (const char *progname,
     close (nulloutfd);
   if (nullinfd >= 0)
     close (nullinfd);
+  free (prog_path_to_free);
 
   if (termsigp != NULL)
     *termsigp = 0;
 
   if (exitcode == -1)
-    {
-      if (exit_on_error || !null_stderr)
-        error (exit_on_error ? EXIT_FAILURE : 0, saved_errno,
-               _("%s subprocess failed"), progname);
-      return 127;
-    }
+    goto fail_with_saved_errno;
 
   return exitcode;
 
@@ -209,6 +258,9 @@ execute (const char *progname,
                                                           "/dev/null", O_RDWR,
                                                           0))
                  != 0)
+          || (directory != NULL
+              && (err = posix_spawn_file_actions_addchdir (&actions,
+                                                           directory)))
           || (slave_process
               && ((err = posix_spawnattr_init (&attrs)) != 0
                   || (attrs_allocated = true,
@@ -218,9 +270,13 @@ execute (const char *progname,
                       || (err = posix_spawnattr_setflags (&attrs,
                                                         POSIX_SPAWN_SETSIGMASK))
                          != 0)))
-          || (err = posix_spawnp (&child, prog_path, &actions,
-                                  attrs_allocated ? &attrs : NULL, prog_argv,
-                                  environ))
+          || (err = (directory != NULL
+                     ? posix_spawn (&child, prog_path, &actions,
+                                    attrs_allocated ? &attrs : NULL, prog_argv,
+                                    environ)
+                     : posix_spawnp (&child, prog_path, &actions,
+                                     attrs_allocated ? &attrs : NULL, prog_argv,
+                                     environ)))
              != 0))
     {
       if (actions_allocated)
@@ -229,12 +285,11 @@ execute (const char *progname,
         posix_spawnattr_destroy (&attrs);
       if (slave_process)
         unblock_fatal_signals ();
+      free (prog_path_to_free);
       if (termsigp != NULL)
         *termsigp = 0;
-      if (exit_on_error || !null_stderr)
-        error (exit_on_error ? EXIT_FAILURE : 0, err,
-               _("%s subprocess failed"), progname);
-      return 127;
+      saved_errno = err;
+      goto fail_with_saved_errno;
     }
   posix_spawn_file_actions_destroy (&actions);
   if (attrs_allocated)
@@ -244,9 +299,18 @@ execute (const char *progname,
       register_slave_subprocess (child);
       unblock_fatal_signals ();
     }
+  free (prog_path_to_free);
 
   return wait_subprocess (child, progname, ignore_sigpipe, null_stderr,
                           slave_process, exit_on_error, termsigp);
 
 #endif
+
+ fail_with_errno:
+  saved_errno = errno;
+ fail_with_saved_errno:
+  if (exit_on_error || !null_stderr)
+    error (exit_on_error ? EXIT_FAILURE : 0, saved_errno,
+           _("%s subprocess failed"), progname);
+  return 127;
 }
index b31c4d1fe47792ba659d4604ffc1d0e6ad5788d0..34fc989668115ec974a626d31e085546594b3ce0 100644 (file)
@@ -32,6 +32,9 @@
    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.
+   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.
@@ -44,6 +47,7 @@
    is called.  See spawn-pipe.h for the reason.  */
 extern int execute (const char *progname,
                     const char *prog_path, char **prog_argv,
+                    const char *directory,
                     bool ignore_sigpipe,
                     bool null_stdin, bool null_stdout, bool null_stderr,
                     bool slave_process, bool exit_on_error,
index 63efc2d78283ce48cd9f92868aa8148c8e7f3284..fec51803940131a141dea1ae438fff5841b140f1 100644 (file)
@@ -343,8 +343,9 @@ compile_using_envjavac (const char *javac,
   argv[1] = "-c";
   argv[2] = command;
   argv[3] = NULL;
-  exitstatus = execute (javac, BOURNE_SHELL, argv, false, false, false,
-                        null_stderr, true, true, NULL);
+  exitstatus = execute (javac, BOURNE_SHELL, argv, NULL,
+                        false, false, false, null_stderr,
+                        true, true, NULL);
   err = (exitstatus != 0);
 
   freea (command);
@@ -425,7 +426,8 @@ compile_using_gcj (const char * const *java_sources,
       free (command);
     }
 
-  exitstatus = execute ("gcj", "gcj", argv, false, false, false, null_stderr,
+  exitstatus = execute ("gcj", "gcj", argv, NULL,
+                        false, false, false, null_stderr,
                         true, true, NULL);
   err = (exitstatus != 0);
 
@@ -496,7 +498,8 @@ compile_using_javac (const char * const *java_sources,
       free (command);
     }
 
-  exitstatus = execute ("javac", "javac", argv, false, false, false,
+  exitstatus = execute ("javac", "javac", argv, NULL,
+                        false, false, false,
                         null_stderr, true, true, NULL);
   err = (exitstatus != 0);
 
@@ -551,8 +554,9 @@ compile_using_jikes (const char * const *java_sources,
       free (command);
     }
 
-  exitstatus = execute ("jikes", "jikes", argv, false, false, false,
-                        null_stderr, true, true, NULL);
+  exitstatus = execute ("jikes", "jikes", argv, NULL,
+                        false, false, false, null_stderr,
+                        true, true, NULL);
   err = (exitstatus != 0);
 
   freea (argv);
@@ -1872,7 +1876,8 @@ is_javac_present (void)
 
       argv[0] = "javac";
       argv[1] = NULL;
-      exitstatus = execute ("javac", "javac", argv, false, false, true, true,
+      exitstatus = execute ("javac", "javac", argv, NULL,
+                            false, false, true, true,
                             true, false, NULL);
       javac_present = (exitstatus == 0 || exitstatus == 1 || exitstatus == 2);
       javac_tested = true;
@@ -2138,7 +2143,8 @@ is_jikes_present (void)
 
       argv[0] = "jikes";
       argv[1] = NULL;
-      exitstatus = execute ("jikes", "jikes", argv, false, false, true, true,
+      exitstatus = execute ("jikes", "jikes", argv, NULL,
+                            false, false, true, true,
                             true, false, NULL);
       jikes_present = (exitstatus == 0 || exitstatus == 1);
       jikes_tested = true;
index a374bff34819ffdf5faf65cadd36146e5e83c111..d486fb87158bbd6adf985786b1d28d6c33070ddf 100644 (file)
@@ -208,7 +208,8 @@ execute_java_class (const char *class_name,
         argv[0] = "gij";
         argv[1] = "--version";
         argv[2] = NULL;
-        exitstatus = execute ("gij", "gij", argv, false, false, true, true,
+        exitstatus = execute ("gij", "gij", argv, NULL,
+                              false, false, true, true,
                               true, false, NULL);
         gij_present = (exitstatus == 0);
         gij_tested = true;
@@ -261,7 +262,8 @@ execute_java_class (const char *class_name,
         argv[0] = "java";
         argv[1] = "-version";
         argv[2] = NULL;
-        exitstatus = execute ("java", "java", argv, false, false, true, true,
+        exitstatus = execute ("java", "java", argv, NULL,
+                              false, false, true, true,
                               true, false, NULL);
         java_present = (exitstatus == 0);
         java_tested = true;
@@ -315,7 +317,8 @@ execute_java_class (const char *class_name,
 
         argv[0] = "jre";
         argv[1] = NULL;
-        exitstatus = execute ("jre", "jre", argv, false, false, true, true,
+        exitstatus = execute ("jre", "jre", argv, NULL,
+                              false, false, true, true,
                               true, false, NULL);
         jre_present = (exitstatus == 0 || exitstatus == 1);
         jre_tested = true;
@@ -372,7 +375,8 @@ execute_java_class (const char *class_name,
         argv[0] = "jview";
         argv[1] = "-?";
         argv[2] = NULL;
-        exitstatus = execute ("jview", "jview", argv, false, false, true, true,
+        exitstatus = execute ("jview", "jview", argv, NULL,
+                              false, false, true, true,
                               true, false, NULL);
         jview_present = (exitstatus == 0 || exitstatus == 1);
         jview_tested = true;
index 52eee5fed014627c826acc9ff1b16a75439f6481..cb04003288d39be625740821064e454913ee7fb6 100644 (file)
@@ -8,15 +8,20 @@ m4/execute.m4
 
 Depends-on:
 dup2
+canonicalize
 environ
 error
 fatal-signal
+filename
+findprog-in
 msvc-nothrow
 gettext-h
 spawn
+posix_spawn
 posix_spawnp
 posix_spawn_file_actions_init
 posix_spawn_file_actions_addopen
+posix_spawn_file_actions_addchdir
 posix_spawn_file_actions_destroy
 posix_spawnattr_init
 posix_spawnattr_setsigmask
index 854b1ad8f63b3c2a2837576f97412ad21934c67b..95621821315587b6676522c6b6eb6bb89519bc8c 100644 (file)
@@ -7,6 +7,7 @@ tests/macros.h
 Depends-on:
 dup2
 fcntl
+mkdir
 msvc-inval
 read-file
 stdint
index 77f99ae0dbea98bfb2bb93f04a9c009e3755c120..8197c300de22af00df0d79a0944b72d6b7bd5b25 100644 (file)
@@ -29,7 +29,7 @@
 /* Get declarations of the native Windows API functions.  */
 # define WIN32_LEAN_AND_MEAN
 # include <windows.h>
-/* Get _get_osfhandle, _isatty.  */
+/* Get _get_osfhandle, _isatty, _chdir, _getcwd.  */
 # include <io.h>
 #endif
 
@@ -41,6 +41,7 @@
 #undef fprintf
 #undef fputs
 #undef fstat
+#undef getcwd
 #undef isatty
 #undef raise
 #undef read
@@ -201,6 +202,23 @@ main (int argc, char *argv[])
         return 4 + 2 * (isatty (10) != 0) + (isatty (11) != 0);
         #endif
       }
+    case 21:
+      /* Check execution in a different directory.  */
+      {
+        char cwd[1024];
+        #if defined _WIN32 && ! defined __CYGWIN__
+        if (_chdir ("..") != 0)
+          return 1;
+        if (_getcwd (cwd, sizeof (cwd)) == NULL)
+          return 2;
+        #else
+        if (chdir ("..") != 0)
+          return 1;
+        if (getcwd (cwd, sizeof (cwd)) == NULL)
+          return 2;
+        #endif
+        return (argc == 3 && strcmp (argv[2], cwd) == 0 ? 0 : 3);
+      }
     default:
       abort ();
     }
index 62357c1e612155b32e3a74df88acba4aa85b2def..755209e68246b4cd10d0c3cd9170fb09ee6d6627 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <sys/stat.h>
 
 #if defined _WIN32 && ! defined __CYGWIN__
-/* Get _isatty.  */
+/* Get _isatty, _getcwd.  */
 # include <io.h>
 #endif
 
@@ -63,7 +64,7 @@ main (int argc, char *argv[])
       {
         /* Check an invocation without arguments.  Check the exit code.  */
         char *prog_argv[2] = { prog_path, NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 40);
       }
@@ -72,7 +73,7 @@ main (int argc, char *argv[])
       {
         /* Check an invocation of a non-existent program.  */
         char *prog_argv[3] = { "./non-existent", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 127);
       }
@@ -96,7 +97,7 @@ main (int argc, char *argv[])
             (char *) "",
             NULL
           };
-        int ret = execute (progname, prog_argv[0], prog_argv,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
       }
@@ -107,7 +108,7 @@ main (int argc, char *argv[])
         /* Check SIGPIPE handling with ignore_sigpipe = false.  */
         char *prog_argv[3] = { prog_path, (char *) "3", NULL };
         int termsig = 0xDEADBEEF;
-        int ret = execute (progname, prog_argv[0], prog_argv,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
                            false, false, false, false, true, false, &termsig);
         ASSERT (ret == 127);
         ASSERT (termsig == SIGPIPE);
@@ -120,7 +121,7 @@ main (int argc, char *argv[])
         /* Check SIGPIPE handling with ignore_sigpipe = true.  */
         char *prog_argv[3] = { prog_path, (char *) "4", NULL };
         int termsig = 0xDEADBEEF;
-        int ret = execute (progname, prog_argv[0], prog_argv,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
                            true, false, false, false, true, false, &termsig);
         ASSERT (ret == 0);
         ASSERT (termsig == SIGPIPE);
@@ -132,7 +133,7 @@ main (int argc, char *argv[])
         /* Check other signal.  */
         char *prog_argv[3] = { prog_path, (char *) "5", NULL };
         int termsig = 0xDEADBEEF;
-        int ret = execute (progname, prog_argv[0], prog_argv,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
                            false, false, false, false, true, false, &termsig);
         #if defined _WIN32 && !defined __CYGWIN__
         ASSERT (ret == 3);
@@ -154,7 +155,7 @@ main (int argc, char *argv[])
         ASSERT (fp != NULL);
 
         char *prog_argv[3] = { prog_path, (char *) "6", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
 
@@ -173,7 +174,7 @@ main (int argc, char *argv[])
         ASSERT (fp != NULL);
 
         char *prog_argv[3] = { prog_path, (char *) "7", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
                            false, true, false, false, true, false, NULL);
         ASSERT (ret == 0);
 
@@ -188,7 +189,7 @@ main (int argc, char *argv[])
         ASSERT (fp != NULL);
 
         char *prog_argv[3] = { prog_path, (char *) "8", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
 
@@ -208,7 +209,7 @@ main (int argc, char *argv[])
         ASSERT (fp != NULL);
 
         char *prog_argv[3] = { prog_path, (char *) "9", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
       }
@@ -220,7 +221,7 @@ main (int argc, char *argv[])
         ASSERT (fp != NULL);
 
         char *prog_argv[3] = { prog_path, (char *) "10", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
                            false, false, true, false, true, false, NULL);
         ASSERT (ret == 0);
 
@@ -240,7 +241,7 @@ main (int argc, char *argv[])
         ASSERT (fp != NULL);
 
         char *prog_argv[3] = { prog_path, (char *) "11", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
 
@@ -260,7 +261,7 @@ main (int argc, char *argv[])
         ASSERT (fp != NULL);
 
         char *prog_argv[3] = { prog_path, (char *) "12", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
       }
@@ -272,7 +273,7 @@ main (int argc, char *argv[])
         ASSERT (fp != NULL);
 
         char *prog_argv[3] = { prog_path, (char *) "13", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
                            false, false, false, true, true, false, NULL);
         ASSERT (ret == 0);
 
@@ -290,7 +291,7 @@ main (int argc, char *argv[])
         /* Check file descriptors >= 3 can be inherited.  */
         ASSERT (dup2 (STDOUT_FILENO, 10) >= 0);
         char *prog_argv[3] = { prog_path, (char *) "14", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
                            true, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
       }
@@ -300,7 +301,7 @@ main (int argc, char *argv[])
         /* Check file descriptors >= 3 can be inherited.  */
         ASSERT (fcntl (STDOUT_FILENO, F_DUPFD, 10) >= 0);
         char *prog_argv[3] = { prog_path, (char *) "15", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
                            true, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
       }
@@ -310,7 +311,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);
         char *prog_argv[3] = { prog_path, (char *) "16", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
                            true, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
       }
@@ -335,7 +336,7 @@ main (int argc, char *argv[])
         /* The file position is now 2.  */
 
         char *prog_argv[3] = { prog_path, (char *) "17", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
 
@@ -359,7 +360,7 @@ main (int argc, char *argv[])
         /* The file position is now 3.  */
 
         char *prog_argv[3] = { prog_path, (char *) "18", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
                            false, false, false, false, true, false, NULL);
         ASSERT (ret == 0);
 
@@ -395,7 +396,7 @@ main (int argc, char *argv[])
         fd_out = 11;
 
         char *prog_argv[3] = { prog_path, (char *) "19", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
                            false, false, false, false, true, false, NULL);
         #if defined _WIN32 && ! defined __CYGWIN__
         ASSERT (ret == 4 + 2 * (_isatty (10) != 0) + (_isatty (11) != 0));
@@ -419,7 +420,7 @@ main (int argc, char *argv[])
         int fd_out = 11;
 
         char *prog_argv[3] = { prog_path, (char *) "20", NULL };
-        int ret = execute (progname, prog_argv[0], prog_argv,
+        int ret = execute (progname, prog_argv[0], prog_argv, NULL,
                            false, false, false, false, true, false, NULL);
         #if defined _WIN32 && ! defined __CYGWIN__
         ASSERT (ret == 4 + 2 * (_isatty (10) != 0) + (_isatty (11) != 0));
@@ -431,6 +432,27 @@ main (int argc, char *argv[])
         close (fd_out);
       }
       break;
+    case 21:
+      {
+        /* Check execution in a different directory.  */
+        rmdir (BASE ".sub");
+        ASSERT (mkdir (BASE ".sub", 0700) == 0);
+
+        char cwd[1024];
+        #if defined _WIN32 && ! defined __CYGWIN__
+        ASSERT (_getcwd (cwd, sizeof (cwd)) != NULL);
+        #else
+        ASSERT (getcwd (cwd, sizeof (cwd)) != NULL);
+        #endif
+
+        char *prog_argv[4] = { prog_path, (char *) "21", cwd, NULL };
+        int ret = execute (progname, prog_argv[0], prog_argv, BASE ".sub",
+                           false, false, false, false, true, false, NULL);
+        ASSERT (ret == 0);
+
+        ASSERT (rmdir (BASE ".sub") == 0);
+      }
+      break;
     default:
       ASSERT (false);
     }
index 1320e76453a3534e1234501d510183a11928acd0..15c8b479b99fc939b056d20fa25302b882670230 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 st=0
-for i in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ; do
+for i in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ; do
   ${CHECKER} ./test-execute-main${EXEEXT} ./test-execute-child${EXEEXT} $i \
     || { echo test-execute.sh: test case $i failed >&2; st=1; }
 done