Patch from Sven Neumann to make the include order consistent. (#71704)
[platform/upstream/glib.git] / glib / gspawn-win32.c
index 11779da..ab6eb2d 100644 (file)
@@ -1,4 +1,4 @@
-/* gspawn.c - Process launching
+/* gspawn-win32.c - Process launching on Win32
  *
  *  Copyright 2000 Red Hat, Inc.
  *
  * Thus, we must in most cases use a helper program to handle closing
  * of (inherited) file descriptors and changing of directory. In fact,
  * we do it all the time.
- *
- * This source file contains the source for that helper program.
- * To compile it, #define GSPAWN_HELPER.
  */
 
 /* Define this to get some logging all the time */
 /* #define G_SPAWN_WIN32_DEBUG */
 
+#include <config.h>
+
+#include "config.h"
+
 #include "glib.h"
+#include "gprintfint.h"
 
 #include <string.h>
 #include <stdlib.h>
+#include <stdio.h>
 
 #include <windows.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <io.h>
 #include <process.h>
+#include <direct.h>
 
-#ifdef _
-#warning "FIXME remove gettext hack"
-#endif
-
-#define _(x) x
+#include "glibintl.h"
 
 #ifdef G_SPAWN_WIN32_DEBUG
   static int debug = 1;
     G_STMT_START                                       \
       {                                                        \
        if (debug == -1)                                \
-         if (getenv ("G_SPAWN_WIN32_DEBUG") != NULL)   \
-           debug = 1;                                  \
-         else                                          \
-           debug = 0;                                  \
+         {                                             \
+           if (getenv ("G_SPAWN_WIN32_DEBUG") != NULL) \
+             debug = 1;                                \
+           else                                        \
+             debug = 0;                                \
+         }                                             \
       }                                                        \
     G_STMT_END
 #endif
@@ -99,7 +101,8 @@ enum {
 
 static gboolean make_pipe            (gint                  p[2],
                                       GError              **error);
-static gboolean fork_exec_with_pipes (gboolean              dont_wait,
+static gboolean do_spawn_with_pipes  (gboolean              dont_wait,
+                                     gboolean              dont_return_handle,
                                      const gchar          *working_directory,
                                       gchar               **argv,
                                       gchar               **envp,
@@ -110,6 +113,7 @@ static gboolean fork_exec_with_pipes (gboolean              dont_wait,
                                       gboolean              child_inherits_stdin,
                                       GSpawnChildSetupFunc  child_setup,
                                       gpointer              user_data,
+                                      gint                 *child_pid,
                                       gint                 *standard_input,
                                       gint                 *standard_output,
                                       gint                 *standard_error,
@@ -125,22 +129,6 @@ g_spawn_error_quark (void)
   return quark;
 }
 
-/**
- * g_spawn_async:
- * @working_directory: child's current working directory, or NULL to inherit parent's
- * @argv: child's argument vector
- * @envp: child's environment, or NULL to inherit parent's
- * @flags: flags from #GSpawnFlags
- * @child_setup: function to run in the child just before exec()
- * @user_data: user data for @child_setup
- * @child_pid: return location for child process ID, or NULL
- * @error: return location for error
- * 
- * See g_spawn_async_with_pipes() for a full description; this function
- * simply calls the g_spawn_async_with_pipes() without any pipes.
- * 
- * Return value: TRUE on success, FALSE if error is set
- **/
 gboolean
 g_spawn_async (const gchar          *working_directory,
                gchar               **argv,
@@ -172,8 +160,13 @@ close_and_invalidate (gint *fd)
 {
   gint ret;
 
-  ret = close (*fd);
-  *fd = -1;
+  if (*fd < 0)
+    return -1;
+  else
+    {
+      ret = close (*fd);
+      *fd = -1;
+    }
 
   return ret;
 }
@@ -190,13 +183,13 @@ read_data (GString     *str,
            GIOChannel  *iochannel,
            GError     **error)
 {
-  GIOError gioerror;
-  gint bytes;
+  GIOStatus giostatus;
+  gssize bytes;
   gchar buf[4096];
 
  again:
   
-  gioerror = g_io_channel_read (iochannel, buf, sizeof (buf), &bytes);
+  giostatus = g_io_channel_read_chars (iochannel, buf, sizeof (buf), &bytes, NULL);
 
   if (bytes == 0)
     return READ_EOF;
@@ -205,9 +198,9 @@ read_data (GString     *str,
       g_string_append_len (str, buf, bytes);
       return READ_OK;
     }
-  else if (gioerror == G_IO_ERROR_AGAIN)
+  else if (giostatus == G_IO_STATUS_AGAIN)
     goto again;
-  else if (gioerror != G_IO_ERROR_NONE)
+  else if (giostatus == G_IO_STATUS_ERROR)
     {
       g_set_error (error,
                    G_SPAWN_ERROR,
@@ -220,32 +213,6 @@ read_data (GString     *str,
     return READ_OK;
 }
 
-/**
- * g_spawn_sync:
- * @working_directory: child's current working directory, or NULL to inherit parent's
- * @argv: child's argument vector
- * @envp: child's environment, or NULL to inherit parent's
- * @flags: flags from #GSpawnFlags
- * @child_setup: function to run in the child just before exec()
- * @user_data: user data for @child_setup
- * @standard_output: return location for child output 
- * @standard_error: return location for child error messages
- * @exit_status: child exit status, as returned by waitpid()
- * @error: return location for error
- *
- * Executes a child synchronously (waits for the child to exit before returning).
- * All output from the child is stored in @standard_output and @standard_error,
- * if those parameters are non-NULL. If @exit_status is non-NULL, the exit status
- * of the child is stored there as it would be by waitpid(); standard UNIX
- * macros such as WIFEXITED() and WEXITSTATUS() must be used to evaluate the
- * exit status. If an error occurs, no data is returned in @standard_output,
- * @standard_error, or @exit_status.
- * 
- * This function calls g_spawn_async_with_pipes() internally; see that function
- * for full details on the other parameters.
- * 
- * Return value: TRUE on success, FALSE if an error was set.
- **/
 gboolean
 g_spawn_sync (const gchar          *working_directory,
               gchar               **argv,
@@ -260,6 +227,7 @@ g_spawn_sync (const gchar          *working_directory,
 {
   gint outpipe = -1;
   gint errpipe = -1;
+  gint pid;
   GIOChannel *outchannel = NULL;
   GIOChannel *errchannel = NULL;
   GPollFD outfd, errfd;
@@ -289,22 +257,24 @@ g_spawn_sync (const gchar          *working_directory,
   if (standard_error)
     *standard_error = NULL;
   
-  if (!fork_exec_with_pipes (FALSE,
-                             working_directory,
-                             argv,
-                             envp,
-                             !(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN),
-                             (flags & G_SPAWN_SEARCH_PATH) != 0,
-                             (flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0,
-                             (flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0,
-                             (flags & G_SPAWN_CHILD_INHERITS_STDIN) != 0,
-                             child_setup,
-                             user_data,
-                             NULL,
-                             standard_output ? &outpipe : NULL,
-                             standard_error ? &errpipe : NULL,
-                            &status,
-                             error))
+  if (!do_spawn_with_pipes (FALSE,
+                           TRUE,
+                           working_directory,
+                           argv,
+                           envp,
+                           !(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN),
+                           (flags & G_SPAWN_SEARCH_PATH) != 0,
+                           (flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0,
+                           (flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0,
+                           (flags & G_SPAWN_CHILD_INHERITS_STDIN) != 0,
+                           child_setup,
+                           user_data,
+                           &pid,
+                           NULL,
+                           standard_output ? &outpipe : NULL,
+                           standard_error ? &errpipe : NULL,
+                           &status,
+                           error))
     return FALSE;
 
   /* Read data from child. */
@@ -315,6 +285,7 @@ g_spawn_sync (const gchar          *working_directory,
     {
       outstr = g_string_new ("");
       outchannel = g_io_channel_win32_new_fd (outpipe);
+      g_io_channel_set_encoding (outchannel, NULL, NULL);
       g_io_channel_win32_make_pollfd (outchannel,
                                      G_IO_IN | G_IO_ERR | G_IO_HUP,
                                      &outfd);
@@ -324,6 +295,7 @@ g_spawn_sync (const gchar          *working_directory,
     {
       errstr = g_string_new ("");
       errchannel = g_io_channel_win32_new_fd (errpipe);
+      g_io_channel_set_encoding (errchannel, NULL, NULL);
       g_io_channel_win32_make_pollfd (errchannel,
                                      G_IO_IN | G_IO_ERR | G_IO_HUP,
                                      &errfd);
@@ -454,81 +426,6 @@ g_spawn_sync (const gchar          *working_directory,
     }
 }
 
-/**
- * g_spawn_async_with_pipes:
- * @working_directory: child's current working directory, or NULL to inherit parent's
- * @argv: child's argument vector
- * @envp: child's environment, or NULL to inherit parent's
- * @flags: flags from #GSpawnFlags
- * @child_setup: function to run in the child just before exec()
- * @user_data: user data for @child_setup
- * @child_pid: return location for child process ID, or NULL
- * @standard_input: return location for file descriptor to write to child's stdin, or NULL
- * @standard_output: return location for file descriptor to read child's stdout, or NULL
- * @standard_error: return location for file descriptor to read child's stderr, or NULL
- * @error: return location for error
- *
- * Executes a child program asynchronously (your program will not
- * block waiting for the child to exit). The child program is
- * specified by the only argument that must be provided, @argv. @argv
- * should be a NULL-terminated array of strings, to be passed as the
- * argument vector for the child. The first string in @argv is of
- * course the name of the program to execute. By default, the name of
- * the program must be a full path; the PATH shell variable will only
- * be searched if you pass the %G_SPAWN_SEARCH_PATH flag.
- *
- * @envp is a NULL-terminated array of strings, where each string
- * has the form <literal>KEY=VALUE</literal>. This will become
- * the child's environment. If @envp is NULL, the child inherits its
- * parent's environment.
- *
- * @flags should be the bitwise OR of any flags you want to affect the
- * function's behavior. The %G_SPAWN_DO_NOT_REAP_CHILD means that the
- * child will not be automatically reaped; you must call waitpid() or
- * handle SIGCHLD yourself, or the child will become a zombie.
- * %G_SPAWN_LEAVE_DESCRIPTORS_OPEN means that the parent's open file
- * descriptors will be inherited by the child; otherwise all
- * descriptors except stdin/stdout/stderr will be closed before
- * calling exec() in the child. %G_SPAWN_SEARCH_PATH means that
- * <literal>argv[0]</literal> need not be an absolute path, it
- * will be looked for in the user's PATH. %G_SPAWN_STDOUT_TO_DEV_NULL
- * means that the child's standad output will be discarded, instead
- * of going to the same location as the parent's standard output.
- * %G_SPAWN_STDERR_TO_DEV_NULL means that the child's standard error
- * will be discarded. %G_SPAWN_CHILD_INHERITS_STDIN means that
- * the child will inherit the parent's standard input (by default,
- * the child's standard input is attached to /dev/null).
- *
- * @child_setup and @user_data are a function and user data to be
- * called in the child after GLib has performed all the setup it plans
- * to perform (including creating pipes, closing file descriptors,
- * etc.) but before calling exec(). That is, @child_setup is called
- * just before calling exec() in the child. Obviously actions taken in
- * this function will only affect the child, not the parent. 
- *
- * If non-NULL, @child_pid will be filled with the child's process
- * ID. You can use the process ID to send signals to the child, or
- * to waitpid() if you specified the %G_SPAWN_DO_NOT_REAP_CHILD flag.
- *
- * If non-NULL, the @standard_input, @standard_output, @standard_error
- * locations will be filled with file descriptors for writing to the child's
- * standard input or reading from its standard output or standard error.
- * The caller of g_spawn_async_with_pipes() must close these file descriptors
- * when they are no longer in use. If these parameters are NULL, the
- * corresponding pipe won't be created.
- *
- * @error can be NULL to ignore errors, or non-NULL to report errors.
- * If an error is set, the function returns FALSE. Errors
- * are reported even if they occur in the child (for example if the
- * executable in <literal>argv[0]</literal> is not found). Typically
- * the <literal>message</literal> field of returned errors should be displayed
- * to users. Possible errors are those from the #G_SPAWN_ERROR domain.
- *
- * If an error occurs, @child_pid, @standard_input, @standard_output,
- * and @standard_error will not be filled with valid values.
- * 
- * Return value: TRUE on success, FALSE if an error was set
- **/
 gboolean
 g_spawn_async_with_pipes (const gchar          *working_directory,
                           gchar               **argv,
@@ -551,43 +448,26 @@ g_spawn_async_with_pipes (const gchar          *working_directory,
   g_return_val_if_fail (standard_input == NULL ||
                         !(flags & G_SPAWN_CHILD_INHERITS_STDIN), FALSE);
   
-  return fork_exec_with_pipes (!(flags & G_SPAWN_DO_NOT_REAP_CHILD),
-                               working_directory,
-                               argv,
-                               envp,
-                               !(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN),
-                               (flags & G_SPAWN_SEARCH_PATH) != 0,
-                               (flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0,
-                               (flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0,
-                               (flags & G_SPAWN_CHILD_INHERITS_STDIN) != 0,
-                               child_setup,
-                               user_data,
-                               standard_input,
-                               standard_output,
-                               standard_error,
-                              NULL,
-                               error);
+  return do_spawn_with_pipes (TRUE,
+                             !(flags & G_SPAWN_DO_NOT_REAP_CHILD),
+                             working_directory,
+                             argv,
+                             envp,
+                             !(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN),
+                             (flags & G_SPAWN_SEARCH_PATH) != 0,
+                             (flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0,
+                             (flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0,
+                             (flags & G_SPAWN_CHILD_INHERITS_STDIN) != 0,
+                             child_setup,
+                             user_data,
+                             child_pid,
+                             standard_input,
+                             standard_output,
+                             standard_error,
+                             NULL,
+                             error);
 }
 
-/**
- * g_spawn_command_line_sync:
- * @command_line: a command line 
- * @standard_output: return location for child output
- * @standard_error: return location for child errors
- * @exit_status: return location for child exit status
- * @error: return location for errors
- *
- * A simple version of g_spawn_sync() with little-used parameters
- * removed, taking a command line instead of an argument vector.  See
- * g_spawn_sync() for full details. @command_line will be parsed by
- * g_shell_parse_argv(). Unlike g_spawn_sync(), the %G_SPAWN_SEARCH_PATH flag
- * is enabled. Note that %G_SPAWN_SEARCH_PATH can have security
- * implications, so consider using g_spawn_sync() directly if
- * appropriate. Possible errors are those from g_spawn_sync() and those
- * from g_shell_parse_argv().
- * 
- * Return value: TRUE on success, FALSE if an error was set
- **/
 gboolean
 g_spawn_command_line_sync (const gchar  *command_line,
                            gchar       **standard_output,
@@ -620,21 +500,6 @@ g_spawn_command_line_sync (const gchar  *command_line,
   return retval;
 }
 
-/**
- * g_spawn_command_line_async:
- * @command_line: a command line
- * @error: return location for errors
- * 
- * A simple version of g_spawn_async() that parses a command line with
- * g_shell_parse_argv() and passes it to g_spawn_async(). Runs a
- * command line in the background. Unlike g_spawn_async(), the
- * %G_SPAWN_SEARCH_PATH flag is enabled, other flags are not. Note
- * that %G_SPAWN_SEARCH_PATH can have security implications, so
- * consider using g_spawn_async() directly if appropriate. Possible
- * errors are those from g_shell_parse_argv() and g_spawn_async().
- * 
- * Return value: TRUE on success, FALSE if error is set.
- **/
 gboolean
 g_spawn_command_line_async (const gchar *command_line,
                             GError     **error)
@@ -663,25 +528,26 @@ g_spawn_command_line_async (const gchar *command_line,
 }
 
 static gint
-do_exec (gboolean              dont_wait,
-        gint                  child_err_report_fd,
-         gint                  stdin_fd,
-         gint                  stdout_fd,
-         gint                  stderr_fd,
-         const gchar          *working_directory,
-         gchar               **argv,
-         gchar               **envp,
-         gboolean              close_descriptors,
-         gboolean              search_path,
-         gboolean              stdout_to_null,
-         gboolean              stderr_to_null,
-         gboolean              child_inherits_stdin,
-         GSpawnChildSetupFunc  child_setup,
-         gpointer              user_data)
+do_spawn (gboolean              dont_wait,
+         gint                  child_err_report_fd,
+         gint                  stdin_fd,
+         gint                  stdout_fd,
+         gint                  stderr_fd,
+         const gchar          *working_directory,
+         gchar               **argv,
+         gchar               **envp,
+         gboolean              close_descriptors,
+         gboolean              search_path,
+         gboolean              stdout_to_null,
+         gboolean              stderr_to_null,
+         gboolean              child_inherits_stdin,
+         GSpawnChildSetupFunc  child_setup,
+         gpointer              user_data)
 {
   gchar **new_argv;
   gchar args[ARG_COUNT][10];
   gint i;
+  int rc;
   int argc = 0;
 
   SETUP_DEBUG();
@@ -692,12 +558,12 @@ do_exec (gboolean              dont_wait,
   new_argv = g_new (gchar *, argc + 1 + ARG_COUNT);
 
   new_argv[0] = "gspawn-win32-helper";
-  sprintf (args[ARG_CHILD_ERR_REPORT], "%d", child_err_report_fd);
+  _g_sprintf (args[ARG_CHILD_ERR_REPORT], "%d", child_err_report_fd);
   new_argv[ARG_CHILD_ERR_REPORT] = args[ARG_CHILD_ERR_REPORT];
 
   if (stdin_fd >= 0)
     {
-      sprintf (args[ARG_STDIN], "%d", stdin_fd);
+      _g_sprintf (args[ARG_STDIN], "%d", stdin_fd);
       new_argv[ARG_STDIN] = args[ARG_STDIN];
     }
   else if (child_inherits_stdin)
@@ -713,7 +579,7 @@ do_exec (gboolean              dont_wait,
 
   if (stdout_fd >= 0)
     {
-      sprintf (args[ARG_STDOUT], "%d", stdout_fd);
+      _g_sprintf (args[ARG_STDOUT], "%d", stdout_fd);
       new_argv[ARG_STDOUT] = args[ARG_STDOUT];
     }
   else if (stdout_to_null)
@@ -727,7 +593,7 @@ do_exec (gboolean              dont_wait,
 
   if (stderr_fd >= 0)
     {
-      sprintf (args[ARG_STDERR], "%d", stderr_fd);
+      _g_sprintf (args[ARG_STDERR], "%d", stderr_fd);
       new_argv[ARG_STDERR] = args[ARG_STDERR];
     }
   else if (stderr_to_null)
@@ -740,7 +606,7 @@ do_exec (gboolean              dont_wait,
     }
 
   if (working_directory && *working_directory)
-    new_argv[ARG_WORKING_DIRECTORY] = working_directory;
+    new_argv[ARG_WORKING_DIRECTORY] = g_strdup (working_directory);
   else
     new_argv[ARG_WORKING_DIRECTORY] = "-";
 
@@ -765,7 +631,7 @@ do_exec (gboolean              dont_wait,
   /* Call user function just before we execute the helper program,
    * which executes the program. Dunno what's the usefulness of this.
    * A child setup function used on Unix probably isn't of much use
-   * as such on Win32, anyhow.
+   * as such on Win32, anyhow
    */
   if (child_setup)
     {
@@ -783,11 +649,9 @@ do_exec (gboolean              dont_wait,
     /* Let's hope envp hasn't mucked with PATH so that
      * gspawn-win32-helper.exe isn't found.
      */
-    spawnvpe (P_NOWAIT, "gspawn-win32-helper", new_argv, envp);
+    rc = spawnvpe (P_NOWAIT, "gspawn-win32-helper", new_argv, envp);
   else
-    spawnvp (P_NOWAIT, "gspawn-win32-helper", new_argv);
-
-  /* FIXME: What if gspawn-win32-helper.exe isn't found? */
+    rc = spawnvp (P_NOWAIT, "gspawn-win32-helper", new_argv);
 
   /* Close the child_err_report_fd and the other process's ends of the
    * pipes in this process, otherwise the reader will never get
@@ -801,9 +665,10 @@ do_exec (gboolean              dont_wait,
   if (stderr_fd >= 0)
     close (stderr_fd);
 
+  g_free (new_argv[ARG_WORKING_DIRECTORY]);
   g_free (new_argv);
 
-  return 0;
+  return rc;
 }
 
 static gboolean
@@ -854,29 +719,30 @@ read_ints (int      fd,
 }
 
 static gboolean
-fork_exec_with_pipes (gboolean              dont_wait,
-                      const gchar          *working_directory,
-                      gchar               **argv,
-                      gchar               **envp,
-                      gboolean              close_descriptors,
-                      gboolean              search_path,
-                      gboolean              stdout_to_null,
-                      gboolean              stderr_to_null,
-                     gboolean              child_inherits_stdin,
-                      GSpawnChildSetupFunc  child_setup,
-                      gpointer              user_data,
-                      gint                 *standard_input,
-                      gint                 *standard_output,
-                      gint                 *standard_error,
-                     gint                 *exit_status,
-                      GError              **error)     
+do_spawn_with_pipes (gboolean              dont_wait,
+                    gboolean              dont_return_handle,
+                    const gchar          *working_directory,
+                    gchar               **argv,
+                    gchar               **envp,
+                    gboolean              close_descriptors,
+                    gboolean              search_path,
+                    gboolean              stdout_to_null,
+                    gboolean              stderr_to_null,
+                    gboolean              child_inherits_stdin,
+                    GSpawnChildSetupFunc  child_setup,
+                    gpointer              user_data,
+                    gint                 *child_pid,
+                    gint                 *standard_input,
+                    gint                 *standard_output,
+                    gint                 *standard_error,
+                    gint                 *exit_status,
+                    GError              **error)     
 {
   gint stdin_pipe[2] = { -1, -1 };
   gint stdout_pipe[2] = { -1, -1 };
   gint stderr_pipe[2] = { -1, -1 };
   gint child_err_report_pipe[2] = { -1, -1 };
-  gint status;
-  gint bytes;
+  gint helper = -1;
   gint buf[2];
   gint n_ints = 0;
   
@@ -892,53 +758,73 @@ fork_exec_with_pipes (gboolean              dont_wait,
   if (standard_error && !make_pipe (stderr_pipe, error))
     goto cleanup_and_fail;
 
-  status = do_exec (dont_wait,
-                   child_err_report_pipe[1],
-                   stdin_pipe[0],
-                   stdout_pipe[1],
-                   stderr_pipe[1],
-                   working_directory,
-                   argv,
-                   envp,
-                   close_descriptors,
-                   search_path,
-                   stdout_to_null,
-                   stderr_to_null,
-                   child_inherits_stdin,
-                   child_setup,
-                   user_data);
+  helper = do_spawn (dont_wait,
+                    child_err_report_pipe[1],
+                    stdin_pipe[0],
+                    stdout_pipe[1],
+                    stderr_pipe[1],
+                    working_directory,
+                    argv,
+                    envp,
+                    close_descriptors,
+                    search_path,
+                    stdout_to_null,
+                    stderr_to_null,
+                    child_inherits_stdin,
+                    child_setup,
+                    user_data);
       
+  /* do_spawn() returns -1 if gspawn-win32-helper couldn't be run */
+  if (helper == -1)
+    {
+      g_set_error (error,
+                  G_SPAWN_ERROR,
+                  G_SPAWN_ERROR_FAILED,
+                  _("Failed to execute helper program"));
+      goto cleanup_and_fail;
+    }
+
   if (!read_ints (child_err_report_pipe[0],
                  buf, 2, &n_ints,
-                 error))
+                 error) ||
+      n_ints != 2)
     goto cleanup_and_fail;
         
-  if (n_ints == 2)
+  /* Error code from gspawn-win32-helper. */
+  switch (buf[0])
     {
-      /* Error from the child. */
-      
-      switch (buf[0])
+    case CHILD_NO_ERROR:
+      if (child_pid && dont_wait && !dont_return_handle)
        {
-       case CHILD_NO_ERROR:
-         break;
-         
-       case CHILD_CHDIR_FAILED:
-         g_set_error (error,
-                      G_SPAWN_ERROR,
-                      G_SPAWN_ERROR_CHDIR,
-                      _("Failed to change to directory '%s' (%s)"),
-                      working_directory,
-                      g_strerror (buf[1]));
-         goto cleanup_and_fail;
-         
-       case CHILD_SPAWN_FAILED:
-         g_set_error (error,
-                      G_SPAWN_ERROR,
-                      G_SPAWN_ERROR_FAILED,
-                      _("Failed to execute child process (%s)"),
-                      g_strerror (buf[1]));
-         goto cleanup_and_fail;
+         /* helper is our HANDLE for gspawn-win32-helper. It has
+          * told us the HANDLE of its child. Duplicate that into
+          * a HANDLE valid in this process.
+          */
+         if (!DuplicateHandle ((HANDLE) helper, (HANDLE) buf[1],
+                               GetCurrentProcess (), (LPHANDLE) child_pid,
+                               0, TRUE, DUPLICATE_SAME_ACCESS))
+           *child_pid = 0;
        }
+      else if (child_pid)
+       *child_pid = 0;
+      break;
+      
+    case CHILD_CHDIR_FAILED:
+      g_set_error (error,
+                  G_SPAWN_ERROR,
+                  G_SPAWN_ERROR_CHDIR,
+                  _("Failed to change to directory '%s' (%s)"),
+                  working_directory,
+                  g_strerror (buf[1]));
+      goto cleanup_and_fail;
+      
+    case CHILD_SPAWN_FAILED:
+      g_set_error (error,
+                  G_SPAWN_ERROR,
+                  G_SPAWN_ERROR_FAILED,
+                  _("Failed to execute child process (%s)"),
+                  g_strerror (buf[1]));
+      goto cleanup_and_fail;
     }
 
   /* Success against all odds! return the information */
@@ -950,11 +836,14 @@ fork_exec_with_pipes (gboolean              dont_wait,
   if (standard_error)
     *standard_error = stderr_pipe[0];
   if (exit_status)
-    *exit_status = status;
+    *exit_status = buf[1];
+  CloseHandle ((HANDLE) helper);
   
   return TRUE;
 
  cleanup_and_fail:
+  if (helper != -1)
+    CloseHandle ((HANDLE) helper);
   close_and_invalidate (&child_err_report_pipe[0]);
   close_and_invalidate (&child_err_report_pipe[1]);
   close_and_invalidate (&stdin_pipe[0]);
@@ -984,208 +873,4 @@ make_pipe (gint     p[2],
     return TRUE;
 }
 
-#else /* GSPAWN_HELPER */
-
-static void
-write_err_and_exit (gint fd,
-                   gint msg)
-{
-  gint en = errno;
-  
-  write (fd, &msg, sizeof(msg));
-  write (fd, &en, sizeof(en));
-  
-  _exit (1);
-}
-
-static void
-write_no_error (gint fd)
-{
-  gint msg = CHILD_NO_ERROR;
-  gint en = 0;
-
-  write (fd, &msg, sizeof(msg));
-  write (fd, &en, sizeof(en));
-}
-
-#ifdef __GNUC__
-#  ifndef _stdcall
-#    define _stdcall  __attribute__((stdcall))
-#  endif
-#endif
-
-/* We build gspawn-win32-helper.exe as a Windows GUI application
- * to avoid any temporarily flashing console windows in case
- * the gspawn function is invoked by a GUI program. Thus, no main()
- * but a WinMain(). We do, however, still use argc and argv tucked
- * away in the global __argc and __argv by the C runtime startup code.
- */
-
-int _stdcall
-WinMain (struct HINSTANCE__ *hInstance,
-        struct HINSTANCE__ *hPrevInstance,
-        char               *lpszCmdLine,
-        int                 nCmdShow)
-{
-  int child_err_report_fd;
-  int i;
-  int fd;
-  int mode;
-  GString *debugstring;
-
-  SETUP_DEBUG();
-
-  if (debug)
-    {
-      debugstring = g_string_new ("");
-
-      g_string_append (debugstring,
-                      g_strdup_printf ("g-spawn-win32-helper: "
-                                       "argc = %d, argv: ",
-                                       __argc));
-      for (i = 0; i < __argc; i++)
-       {
-         if (i > 0)
-           g_string_append (debugstring, " ");
-         g_string_append (debugstring, __argv[i]);
-       }
-      
-      MessageBox (NULL, debugstring->str, "gspawn-win32-helper", 0);
-    }
-
-  g_assert (__argc >= ARG_COUNT);
-
-  /* argv[ARG_CHILD_ERR_REPORT] is the file descriptor onto which
-   * write error messages.
-   */
-  child_err_report_fd = atoi (__argv[ARG_CHILD_ERR_REPORT]);
-
-  /* argv[ARG_STDIN..ARG_STDERR] are the file descriptors that should
-   * be dup2'd to stdin, stdout and stderr, '-' if the corresponding
-   * std* should be let alone, and 'z' if it should be connected to
-   * the bit bucket NUL:.
-   */
-  if (__argv[ARG_STDIN][0] == '-')
-    ; /* Nothing */
-  else if (__argv[ARG_STDIN][0] == 'z')
-    {
-      fd = open ("NUL:", O_RDONLY);
-      if (fd != 0)
-       {
-         dup2 (fd, 0);
-         close (fd);
-       }
-    }
-  else
-    {
-      fd = atoi (__argv[ARG_STDIN]);
-      if (fd != 0)
-       {
-         dup2 (fd, 0);
-         close (fd);
-       }
-    }
-
-  if (__argv[ARG_STDOUT][0] == '-')
-    ; /* Nothing */
-  else if (__argv[ARG_STDOUT][0] == 'z')
-    {
-      fd = open ("NUL:", O_WRONLY);
-      if (fd != 1)
-       {
-         dup2 (fd, 1);
-         close (fd);
-       }
-    }
-  else
-    {
-      fd = atoi (__argv[ARG_STDOUT]);
-      if (fd != 1)
-       {
-         dup2 (fd, 1);
-         close (fd);
-       }
-    }
-
-  if (__argv[ARG_STDERR][0] == '-')
-    ; /* Nothing */
-  else if (__argv[ARG_STDERR][0] == 'z')
-    {
-      fd = open ("NUL:", O_WRONLY);
-      if (fd != 2)
-       {
-         dup2 (fd, 2);
-         close (fd);
-       }
-    }
-  else
-    {
-      fd = atoi (__argv[ARG_STDERR]);
-      if (fd != 2)
-       {
-         dup2 (fd, 2);
-         close (fd);
-       }
-    }
-
-  /* __argv[ARG_WORKING_DIRECTORY] is the directory in which to run the
-   * process.  If "-", don't change directory.
-   */
-  if (__argv[ARG_WORKING_DIRECTORY][0] == '-' &&
-      __argv[ARG_WORKING_DIRECTORY][1] == 0)
-    ; /* Nothing */
-  else if (chdir (__argv[ARG_WORKING_DIRECTORY]) < 0)
-    write_err_and_exit (child_err_report_fd,
-                       CHILD_CHDIR_FAILED);
-
-  /* __argv[ARG_CLOSE_DESCRIPTORS] is "y" if file descriptors from 3
-   *  upwards should be closed
-   */
-
-  if (__argv[ARG_CLOSE_DESCRIPTORS][0] == 'y')
-    for (i = 3; i < 1000; i++) /* FIXME real limit? */
-      if (i != child_err_report_fd)
-       close (i);
-
-  /* __argv[ARG_WAIT] is "w" to wait for the program to exit */
-
-  if (__argv[ARG_WAIT][0] == 'w')
-    mode = P_WAIT;
-  else
-    mode = P_NOWAIT;
-
-  /* __argv[ARG_USE_PATH] is "y" to use PATH, otherwise not */
-
-  /* __argv[ARG_PROGRAM] is program file to run,
-   * __argv[ARG_PROGRAM+1]... is its __argv.
-   */
-
-  if (debug)
-    {
-      debugstring = g_string_new ("");
-      g_string_append (debugstring,
-                      g_strdup_printf ("calling %s on program %s, __argv: ",
-                                       (__argv[ARG_USE_PATH][0] == 'y' ?
-                                        "spawnvp" : "spawnv"),
-                                       __argv[ARG_PROGRAM]));
-      i = ARG_PROGRAM+1;
-      while (__argv[i])
-       g_string_append (debugstring, __argv[i++]);
-      MessageBox (NULL, debugstring->str, "gspawn-win32-helper", 0);
-    }
-
-  if (__argv[ARG_USE_PATH][0] == 'y')
-    {
-      if (spawnvp (mode, __argv[ARG_PROGRAM], __argv+ARG_PROGRAM) < 0)
-       write_err_and_exit (child_err_report_fd, CHILD_SPAWN_FAILED);
-    }
-  else
-    {
-      if (spawnv (mode, __argv[ARG_PROGRAM], __argv+ARG_PROGRAM) < 0)
-       write_err_and_exit (child_err_report_fd, CHILD_SPAWN_FAILED);
-    }
-
-  return 0;
-}
-
-#endif /* GSPAWN_HELPER */
+#endif /* !GSPAWN_HELPER */