[kdbus] sync with kdbus (kdbus.h - commit: 5ae1ecac44cb)
[platform/upstream/glib.git] / glib / gspawn-win32.c
index 11779da..d98aba1 100644 (file)
@@ -1,6 +1,7 @@
-/* gspawn.c - Process launching
+/* gspawn-win32.c - Process launching on Win32
  *
  *  Copyright 2000 Red Hat, Inc.
+ *  Copyright 2003 Tor Lillqvist
  *
  * GLib is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
  *   before starting the child process. (There might be several threads
  *   running, and the current directory is common for all threads.)
  *
- * 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.
+ * Thus, we must in many cases use a helper program to handle closing
+ * of (inherited) file descriptors and changing of directory. The
+ * helper process is also needed if the standard input, standard
+ * output, or standard error of the process to be run are supposed to
+ * be redirected somewhere.
  *
- * This source file contains the source for that helper program.
- * To compile it, #define GSPAWN_HELPER.
+ * The structure of the source code in this file is a mess, I know.
  */
 
 /* Define this to get some logging all the time */
 /* #define G_SPAWN_WIN32_DEBUG */
 
+#include "config.h"
+
 #include "glib.h"
+#include "glib-private.h"
+#include "gprintfint.h"
+#include "glibintl.h"
+#include "gthread.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>
+#include <wchar.h>
 
-#ifdef _
-#warning "FIXME remove gettext hack"
-#endif
-
-#define _(x) x
-
+#ifndef GSPAWN_HELPER
 #ifdef G_SPAWN_WIN32_DEBUG
   static int debug = 1;
   #define SETUP_DEBUG() /* empty */
-
 #else
   static int debug = -1;
   #define SETUP_DEBUG()                                        \
     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
+#endif
 
 enum
 {
@@ -84,6 +92,7 @@ enum
 
 enum {
   ARG_CHILD_ERR_REPORT = 1,
+  ARG_HELPER_SYNC,
   ARG_STDIN,
   ARG_STDOUT,
   ARG_STDERR,
@@ -95,87 +104,148 @@ enum {
   ARG_COUNT = ARG_PROGRAM
 };
 
+static int
+dup_noninherited (int fd,
+                 int mode)
+{
+  HANDLE filehandle;
+
+  DuplicateHandle (GetCurrentProcess (), (LPHANDLE) _get_osfhandle (fd),
+                  GetCurrentProcess (), &filehandle,
+                  0, FALSE, DUPLICATE_SAME_ACCESS);
+  close (fd);
+  return _open_osfhandle ((gintptr) filehandle, mode | _O_NOINHERIT);
+}
+
 #ifndef GSPAWN_HELPER
 
-static gboolean make_pipe            (gint                  p[2],
-                                      GError              **error);
-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);
-
-GQuark
-g_spawn_error_quark (void)
+#ifdef _WIN64
+#define HELPER_PROCESS "gspawn-win64-helper"
+#else
+#define HELPER_PROCESS "gspawn-win32-helper"
+#endif
+
+static gchar *
+protect_argv_string (const gchar *string)
+{
+  const gchar *p = string;
+  gchar *retval, *q;
+  gint len = 0;
+  gboolean need_dblquotes = FALSE;
+  while (*p)
+    {
+      if (*p == ' ' || *p == '\t')
+       need_dblquotes = TRUE;
+      else if (*p == '"')
+       len++;
+      else if (*p == '\\')
+       {
+         const gchar *pp = p;
+         while (*pp && *pp == '\\')
+           pp++;
+         if (*pp == '"')
+           len++;
+       }
+      len++;
+      p++;
+    }
+  
+  q = retval = g_malloc (len + need_dblquotes*2 + 1);
+  p = string;
+
+  if (need_dblquotes)
+    *q++ = '"';
+  
+  while (*p)
+    {
+      if (*p == '"')
+       *q++ = '\\';
+      else if (*p == '\\')
+       {
+         const gchar *pp = p;
+         while (*pp && *pp == '\\')
+           pp++;
+         if (*pp == '"')
+           *q++ = '\\';
+       }
+      *q++ = *p;
+      p++;
+    }
+  
+  if (need_dblquotes)
+    *q++ = '"';
+  *q++ = '\0';
+
+  return retval;
+}
+
+static gint
+protect_argv (gchar  **argv,
+             gchar ***new_argv)
 {
-  static GQuark quark = 0;
-  if (quark == 0)
-    quark = g_quark_from_static_string ("g-exec-error-quark");
-  return quark;
+  gint i;
+  gint argc = 0;
+  
+  while (argv[argc])
+    ++argc;
+  *new_argv = g_new (gchar *, argc+1);
+
+  /* Quote each argv element if necessary, so that it will get
+   * reconstructed correctly in the C runtime startup code.  Note that
+   * the unquoting algorithm in the C runtime is really weird, and
+   * rather different than what Unix shells do. See stdargv.c in the C
+   * runtime sources (in the Platform SDK, in src/crt).
+   *
+   * Note that an new_argv[0] constructed by this function should
+   * *not* be passed as the filename argument to a spawn* or exec*
+   * family function. That argument should be the real file name
+   * without any quoting.
+   */
+  for (i = 0; i < argc; i++)
+    (*new_argv)[i] = protect_argv_string (argv[i]);
+
+  (*new_argv)[argc] = NULL;
+
+  return argc;
 }
 
-/**
- * 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
- **/
+G_DEFINE_QUARK (g-exec-error-quark, g_spawn_error)
+G_DEFINE_QUARK (g-spawn-exit-error-quark, g_spawn_exit_error)
+
 gboolean
-g_spawn_async (const gchar          *working_directory,
-               gchar               **argv,
-               gchar               **envp,
-               GSpawnFlags           flags,
-               GSpawnChildSetupFunc  child_setup,
-               gpointer              user_data,
-               gint                 *child_pid,
-               GError              **error)
+g_spawn_async_utf8 (const gchar          *working_directory,
+                   gchar               **argv,
+                   gchar               **envp,
+                   GSpawnFlags           flags,
+                   GSpawnChildSetupFunc  child_setup,
+                   gpointer              user_data,
+                   GPid                 *child_handle,
+                   GError              **error)
 {
   g_return_val_if_fail (argv != NULL, FALSE);
   
-  return g_spawn_async_with_pipes (working_directory,
-                                   argv, envp,
-                                   flags,
-                                   child_setup,
-                                   user_data,
-                                   child_pid,
-                                   NULL, NULL, NULL,
-                                   error);
+  return g_spawn_async_with_pipes_utf8 (working_directory,
+                                       argv, envp,
+                                       flags,
+                                       child_setup,
+                                       user_data,
+                                       child_handle,
+                                       NULL, NULL, NULL,
+                                       error);
 }
 
 /* Avoids a danger in threaded situations (calling close()
  * on a file descriptor twice, and another thread has
  * re-opened it since the first close)
  */
-static gint
+static void
 close_and_invalidate (gint *fd)
 {
-  gint ret;
+  if (*fd < 0)
+    return;
 
-  ret = close (*fd);
+  close (*fd);
   *fd = -1;
-
-  return ret;
 }
 
 typedef enum
@@ -190,13 +260,13 @@ read_data (GString     *str,
            GIOChannel  *iochannel,
            GError     **error)
 {
-  GIOError gioerror;
-  gint bytes;
+  GIOStatus giostatus;
+  gsize 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,14 +275,12 @@ 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,
-                   G_SPAWN_ERROR_READ,
-                   _("Failed to read data from child process"));
+      g_set_error_literal (error, G_SPAWN_ERROR, G_SPAWN_ERROR_READ,
+                           _("Failed to read data from child process"));
       
       return READ_FAILED;
     }
@@ -220,487 +288,359 @@ 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,
-              gchar               **envp,
-              GSpawnFlags           flags,
-              GSpawnChildSetupFunc  child_setup,
-              gpointer              user_data,
-              gchar               **standard_output,
-              gchar               **standard_error,
-              gint                 *exit_status,
-              GError              **error)     
+static gboolean
+make_pipe (gint     p[2],
+           GError **error)
 {
-  gint outpipe = -1;
-  gint errpipe = -1;
-  GIOChannel *outchannel = NULL;
-  GIOChannel *errchannel = NULL;
-  GPollFD outfd, errfd;
-  GPollFD fds[2];
-  gint nfds;
-  gint outindex = -1;
-  gint errindex = -1;
-  gint ret;
-  GString *outstr = NULL;
-  GString *errstr = NULL;
-  gboolean failed;
-  gint status;
-  
-  g_return_val_if_fail (argv != NULL, FALSE);
-  g_return_val_if_fail (!(flags & G_SPAWN_DO_NOT_REAP_CHILD), FALSE);
-  g_return_val_if_fail (standard_output == NULL ||
-                        !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
-  g_return_val_if_fail (standard_error == NULL ||
-                        !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
-  
-  /* Just to ensure segfaults if callers try to use
-   * these when an error is reported.
-   */
-  if (standard_output)
-    *standard_output = NULL;
-
-  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))
-    return FALSE;
-
-  /* Read data from child. */
-  
-  failed = FALSE;
-
-  if (outpipe >= 0)
-    {
-      outstr = g_string_new ("");
-      outchannel = g_io_channel_win32_new_fd (outpipe);
-      g_io_channel_win32_make_pollfd (outchannel,
-                                     G_IO_IN | G_IO_ERR | G_IO_HUP,
-                                     &outfd);
-    }
-      
-  if (errpipe >= 0)
+  if (_pipe (p, 4096, _O_BINARY) < 0)
     {
-      errstr = g_string_new ("");
-      errchannel = g_io_channel_win32_new_fd (errpipe);
-      g_io_channel_win32_make_pollfd (errchannel,
-                                     G_IO_IN | G_IO_ERR | G_IO_HUP,
-                                     &errfd);
+      int errsv = errno;
+
+      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                   _("Failed to create pipe for communicating with child process (%s)"),
+                   g_strerror (errsv));
+      return FALSE;
     }
+  else
+    return TRUE;
+}
 
-  /* Read data until we get EOF on both pipes. */
-  while (!failed &&
-         (outpipe >= 0 ||
-          errpipe >= 0))
+/* The helper process writes a status report back to us, through a
+ * pipe, consisting of two ints.
+ */
+static gboolean
+read_helper_report (int      fd,
+                   gintptr  report[2],
+                   GError **error)
+{
+  gint bytes = 0;
+  
+  while (bytes < sizeof(gintptr)*2)
     {
-      nfds = 0;
-      if (outpipe >= 0)
-       {
-         fds[nfds] = outfd;
-         outindex = nfds;
-         nfds++;
-       }
-      if (errpipe >= 0)
-       {
-         fds[nfds] = errfd;
-         errindex = nfds;
-         nfds++;
-       }
+      gint chunk;
 
       if (debug)
-       g_print ("%s:g_spawn_sync: calling g_io_channel_win32_poll, nfds=%d\n",
-                __FILE__, nfds);
+       g_print ("%s:read_helper_report: read %" G_GSIZE_FORMAT "...\n",
+                __FILE__,
+                sizeof(gintptr)*2 - bytes);
 
-      ret = g_io_channel_win32_poll (fds, nfds, -1);
+      chunk = read (fd, ((gchar*)report) + bytes,
+                   sizeof(gintptr)*2 - bytes);
 
-      if (ret < 0)
+      if (debug)
+       g_print ("...got %d bytes\n", chunk);
+          
+      if (chunk < 0)
         {
-          failed = TRUE;
+          int errsv = errno;
 
-          g_set_error (error,
-                       G_SPAWN_ERROR,
-                       G_SPAWN_ERROR_READ,
-                       _("Unexpected error in g_io_channel_win32_poll() reading data from a child process"));
-              
-          break;
+          /* Some weird shit happened, bail out */
+          g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                       _("Failed to read from child pipe (%s)"),
+                       g_strerror (errsv));
+
+          return FALSE;
         }
+      else if (chunk == 0)
+       {
+         g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                      _("Failed to read from child pipe (%s)"),
+                      "EOF");
+         break; /* EOF */
+       }
+      else
+       bytes += chunk;
+    }
 
-      if (outpipe >= 0 && (fds[outindex].revents & G_IO_IN))
-        {
-          switch (read_data (outstr, outchannel, error))
-            {
-            case READ_FAILED:
-             if (debug)
-               g_print ("g_spawn_sync: outchannel: READ_FAILED\n");
-              failed = TRUE;
-              break;
-            case READ_EOF:
-             if (debug)
-               g_print ("g_spawn_sync: outchannel: READ_EOF\n");
-              g_io_channel_unref (outchannel);
-             outchannel = NULL;
-              close_and_invalidate (&outpipe);
-              break;
-            default:
-             if (debug)
-               g_print ("g_spawn_sync: outchannel: OK\n");
-              break;
-            }
+  if (bytes < sizeof(gintptr)*2)
+    return FALSE;
 
-          if (failed)
-            break;
-        }
+  return TRUE;
+}
 
-      if (errpipe >= 0 && (fds[errindex].revents & G_IO_IN))
-        {
-          switch (read_data (errstr, errchannel, error))
-            {
-            case READ_FAILED:
-             if (debug)
-               g_print ("g_spawn_sync: errchannel: READ_FAILED\n");
-              failed = TRUE;
-              break;
-            case READ_EOF:
-             if (debug)
-               g_print ("g_spawn_sync: errchannel: READ_EOF\n");
-             g_io_channel_unref (errchannel);
-             errchannel = NULL;
-              close_and_invalidate (&errpipe);
-              break;
-            default:
-             if (debug)
-               g_print ("g_spawn_sync: errchannel: OK\n");
-              break;
-            }
+static void
+set_child_error (gintptr      report[2],
+                const gchar *working_directory,
+                GError     **error)
+{
+  switch (report[0])
+    {
+    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 (report[1]));
+      break;
+    case CHILD_SPAWN_FAILED:
+      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                  _("Failed to execute child process (%s)"),
+                  g_strerror (report[1]));
+      break;
+    default:
+      g_assert_not_reached ();
+    }
+}
 
-          if (failed)
-            break;
-        }
+static gboolean
+utf8_charv_to_wcharv (char     **utf8_charv,
+                     wchar_t ***wcharv,
+                     int       *error_index,
+                     GError   **error)
+{
+  wchar_t **retval = NULL;
+
+  *wcharv = NULL;
+  if (utf8_charv != NULL)
+    {
+      int n = 0, i;
+
+      while (utf8_charv[n])
+       n++;
+      retval = g_new (wchar_t *, n + 1);
+
+      for (i = 0; i < n; i++)
+       {
+         retval[i] = g_utf8_to_utf16 (utf8_charv[i], -1, NULL, NULL, error);
+         if (retval[i] == NULL)
+           {
+             if (error_index)
+               *error_index = i;
+             while (i)
+               g_free (retval[--i]);
+             g_free (retval);
+             return FALSE;
+           }
+       }
+           
+      retval[n] = NULL;
     }
+  *wcharv = retval;
+  return TRUE;
+}
 
-  /* These should only be open still if we had an error.  */
-  
-  if (outchannel != NULL)
-    g_io_channel_unref (outchannel);
-  if (errchannel != NULL)
-    g_io_channel_unref (errchannel);
-  if (outpipe >= 0)
-    close_and_invalidate (&outpipe);
-  if (errpipe >= 0)
-    close_and_invalidate (&errpipe);
+static gboolean
+do_spawn_directly (gint                 *exit_status,
+                  gboolean              do_return_handle,
+                  GSpawnFlags           flags,
+                  gchar               **argv,
+                  char                **envp,
+                  char                **protected_argv,
+                  GPid                 *child_handle,
+                  GError              **error)     
+{
+  const int mode = (exit_status == NULL) ? P_NOWAIT : P_WAIT;
+  char **new_argv;
+  gintptr rc = -1;
+  int saved_errno;
+  GError *conv_error = NULL;
+  gint conv_error_index;
+  wchar_t *wargv0, **wargv, **wenvp;
+
+  new_argv = (flags & G_SPAWN_FILE_AND_ARGV_ZERO) ? protected_argv + 1 : protected_argv;
+      
+  wargv0 = g_utf8_to_utf16 (argv[0], -1, NULL, NULL, &conv_error);
+  if (wargv0 == NULL)
+    {
+      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                  _("Invalid program name: %s"),
+                  conv_error->message);
+      g_error_free (conv_error);
+      
+      return FALSE;
+    }
   
-  if (failed)
+  if (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
     {
-      if (outstr)
-        g_string_free (outstr, TRUE);
-      if (errstr)
-        g_string_free (errstr, TRUE);
+      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                  _("Invalid string in argument vector at %d: %s"),
+                  conv_error_index, conv_error->message);
+      g_error_free (conv_error);
+      g_free (wargv0);
 
       return FALSE;
     }
-  else
+
+  if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
     {
-      if (exit_status)
-        *exit_status = status;
-      
-      if (standard_output)        
-        *standard_output = g_string_free (outstr, FALSE);
+      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                  _("Invalid string in environment: %s"),
+                  conv_error->message);
+      g_error_free (conv_error);
+      g_free (wargv0);
+      g_strfreev ((gchar **) wargv);
 
-      if (standard_error)
-        *standard_error = g_string_free (errstr, FALSE);
+      return FALSE;
+    }
 
-      return TRUE;
+  if (flags & G_SPAWN_SEARCH_PATH)
+    if (wenvp != NULL)
+      rc = _wspawnvpe (mode, wargv0, (const wchar_t **) wargv, (const wchar_t **) wenvp);
+    else
+      rc = _wspawnvp (mode, wargv0, (const wchar_t **) wargv);
+  else
+    if (wenvp != NULL)
+      rc = _wspawnve (mode, wargv0, (const wchar_t **) wargv, (const wchar_t **) wenvp);
+    else
+      rc = _wspawnv (mode, wargv0, (const wchar_t **) wargv);
+
+  g_free (wargv0);
+  g_strfreev ((gchar **) wargv);
+  g_strfreev ((gchar **) wenvp);
+
+  saved_errno = errno;
+
+  if (rc == -1 && saved_errno != 0)
+    {
+      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                  _("Failed to execute child process (%s)"),
+                  g_strerror (saved_errno));
+      return FALSE;
     }
-}
 
-/**
- * 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,
-                          gchar               **envp,
-                          GSpawnFlags           flags,
-                          GSpawnChildSetupFunc  child_setup,
-                          gpointer              user_data,
-                          gint                 *child_pid,
-                          gint                 *standard_input,
-                          gint                 *standard_output,
-                          gint                 *standard_error,
-                          GError              **error)
-{
-  g_return_val_if_fail (argv != NULL, FALSE);
-  g_return_val_if_fail (standard_output == NULL ||
-                        !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
-  g_return_val_if_fail (standard_error == NULL ||
-                        !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
-  /* can't inherit stdin if we have an input pipe. */
-  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);
-}
-
-/**
- * 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,
-                           gchar       **standard_error,
-                           gint         *exit_status,
-                           GError      **error)
-{
-  gboolean retval;
-  gchar **argv = 0;
-
-  g_return_val_if_fail (command_line != NULL, FALSE);
-  
-  if (!g_shell_parse_argv (command_line,
-                           NULL, &argv,
-                           error))
-    return FALSE;
-  
-  retval = g_spawn_sync (NULL,
-                         argv,
-                         NULL,
-                         G_SPAWN_SEARCH_PATH,
-                         NULL,
-                         NULL,
-                         standard_output,
-                         standard_error,
-                         exit_status,
-                         error);
-  g_strfreev (argv);
+  if (exit_status == NULL)
+    {
+      if (child_handle && do_return_handle)
+       *child_handle = (GPid) rc;
+      else
+       {
+         CloseHandle ((HANDLE) rc);
+         if (child_handle)
+           *child_handle = 0;
+       }
+    }
+  else
+    *exit_status = rc;
 
-  return retval;
+  return TRUE;
 }
 
-/**
- * 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)
+static gboolean
+do_spawn_with_pipes (gint                 *exit_status,
+                    gboolean              do_return_handle,
+                    const gchar          *working_directory,
+                    gchar               **argv,
+                    char                **envp,
+                    GSpawnFlags           flags,
+                    GSpawnChildSetupFunc  child_setup,
+                    GPid                 *child_handle,
+                    gint                 *standard_input,
+                    gint                 *standard_output,
+                    gint                 *standard_error,
+                    gint                 *err_report,
+                    GError              **error)     
 {
-  gboolean retval;
-  gchar **argv = 0;
-
-  g_return_val_if_fail (command_line != NULL, FALSE);
-
-  if (!g_shell_parse_argv (command_line,
-                           NULL, &argv,
-                           error))
-    return FALSE;
-  
-  retval = g_spawn_async (NULL,
-                          argv,
-                          NULL,
-                          G_SPAWN_SEARCH_PATH,
-                          NULL,
-                          NULL,
-                          NULL,
-                          error);
-  g_strfreev (argv);
-
-  return retval;
-}
+  char **protected_argv;
+  char args[ARG_COUNT][10];
+  char **new_argv;
+  int i;
+  gintptr rc = -1;
+  int saved_errno;
+  int argc;
+  int stdin_pipe[2] = { -1, -1 };
+  int stdout_pipe[2] = { -1, -1 };
+  int stderr_pipe[2] = { -1, -1 };
+  int child_err_report_pipe[2] = { -1, -1 };
+  int helper_sync_pipe[2] = { -1, -1 };
+  gintptr helper_report[2];
+  static gboolean warned_about_child_setup = FALSE;
+  GError *conv_error = NULL;
+  gint conv_error_index;
+  gchar *helper_process;
+  CONSOLE_CURSOR_INFO cursor_info;
+  wchar_t *whelper, **wargv, **wenvp;
+  gchar *glib_dll_directory;
+
+  if (child_setup && !warned_about_child_setup)
+    {
+      warned_about_child_setup = TRUE;
+      g_warning ("passing a child setup function to the g_spawn functions is pointless on Windows and it is ignored");
+    }
 
-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)
-{
-  gchar **new_argv;
-  gchar args[ARG_COUNT][10];
-  gint i;
-  int argc = 0;
+  argc = protect_argv (argv, &protected_argv);
 
-  SETUP_DEBUG();
+  if (!standard_input && !standard_output && !standard_error &&
+      (flags & G_SPAWN_CHILD_INHERITS_STDIN) &&
+      !(flags & G_SPAWN_STDOUT_TO_DEV_NULL) &&
+      !(flags & G_SPAWN_STDERR_TO_DEV_NULL) &&
+      (working_directory == NULL || !*working_directory) &&
+      (flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN))
+    {
+      /* We can do without the helper process */
+      gboolean retval =
+       do_spawn_directly (exit_status, do_return_handle, flags,
+                          argv, envp, protected_argv,
+                          child_handle, error);
+      g_strfreev (protected_argv);
+      return retval;
+    }
 
-  while (argv[argc])
-    ++argc;
+  if (standard_input && !make_pipe (stdin_pipe, error))
+    goto cleanup_and_fail;
+  
+  if (standard_output && !make_pipe (stdout_pipe, error))
+    goto cleanup_and_fail;
+  
+  if (standard_error && !make_pipe (stderr_pipe, error))
+    goto cleanup_and_fail;
+  
+  if (!make_pipe (child_err_report_pipe, error))
+    goto cleanup_and_fail;
+  
+  if (!make_pipe (helper_sync_pipe, error))
+    goto cleanup_and_fail;
+  
+  new_argv = g_new (char *, argc + 1 + ARG_COUNT);
+  if (GetConsoleWindow () != NULL)
+    helper_process = HELPER_PROCESS "-console.exe";
+  else
+    helper_process = HELPER_PROCESS ".exe";
+  
+  glib_dll_directory = _glib_get_dll_directory ();
+  if (glib_dll_directory != NULL)
+    {
+      helper_process = g_build_filename (glib_dll_directory, helper_process, NULL);
+      g_free (glib_dll_directory);
+    }
+  else
+    helper_process = g_strdup (helper_process);
 
-  new_argv = g_new (gchar *, argc + 1 + ARG_COUNT);
+  new_argv[0] = protect_argv_string (helper_process);
 
-  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_pipe[1]);
   new_argv[ARG_CHILD_ERR_REPORT] = args[ARG_CHILD_ERR_REPORT];
+  
+  /* Make the read end of the child error report pipe
+   * noninherited. Otherwise it will needlessly be inherited by the
+   * helper process, and the started actual user process. As such that
+   * shouldn't harm, but it is unnecessary.
+   */
+  child_err_report_pipe[0] = dup_noninherited (child_err_report_pipe[0], _O_RDONLY);
+
+  if (flags & G_SPAWN_FILE_AND_ARGV_ZERO)
+    {
+      /* Overload ARG_CHILD_ERR_REPORT to also encode the
+       * G_SPAWN_FILE_AND_ARGV_ZERO functionality.
+       */
+      strcat (args[ARG_CHILD_ERR_REPORT], "#");
+    }
+  
+  _g_sprintf (args[ARG_HELPER_SYNC], "%d", helper_sync_pipe[0]);
+  new_argv[ARG_HELPER_SYNC] = args[ARG_HELPER_SYNC];
+  
+  /* Make the write end of the sync pipe noninherited. Otherwise the
+   * helper process will inherit it, and thus if this process happens
+   * to crash before writing the sync byte to the pipe, the helper
+   * process won't read but won't get any EOF either, as it has the
+   * write end open itself.
+   */
+  helper_sync_pipe[1] = dup_noninherited (helper_sync_pipe[1], _O_WRONLY);
 
-  if (stdin_fd >= 0)
+  if (standard_input)
     {
-      sprintf (args[ARG_STDIN], "%d", stdin_fd);
+      _g_sprintf (args[ARG_STDIN], "%d", stdin_pipe[0]);
       new_argv[ARG_STDIN] = args[ARG_STDIN];
     }
-  else if (child_inherits_stdin)
+  else if (flags & G_SPAWN_CHILD_INHERITS_STDIN)
     {
       /* Let stdin be alone */
       new_argv[ARG_STDIN] = "-";
@@ -710,13 +650,13 @@ do_exec (gboolean              dont_wait,
       /* Keep process from blocking on a read of stdin */
       new_argv[ARG_STDIN] = "z";
     }
-
-  if (stdout_fd >= 0)
+  
+  if (standard_output)
     {
-      sprintf (args[ARG_STDOUT], "%d", stdout_fd);
+      _g_sprintf (args[ARG_STDOUT], "%d", stdout_pipe[1]);
       new_argv[ARG_STDOUT] = args[ARG_STDOUT];
     }
-  else if (stdout_to_null)
+  else if (flags & G_SPAWN_STDOUT_TO_DEV_NULL)
     {
       new_argv[ARG_STDOUT] = "z";
     }
@@ -724,13 +664,13 @@ do_exec (gboolean              dont_wait,
     {
       new_argv[ARG_STDOUT] = "-";
     }
-
-  if (stderr_fd >= 0)
+  
+  if (standard_error)
     {
-      sprintf (args[ARG_STDERR], "%d", stderr_fd);
+      _g_sprintf (args[ARG_STDERR], "%d", stderr_pipe[1]);
       new_argv[ARG_STDERR] = args[ARG_STDERR];
     }
-  else if (stderr_to_null)
+  else if (flags & G_SPAWN_STDERR_TO_DEV_NULL)
     {
       new_argv[ARG_STDERR] = "z";
     }
@@ -738,205 +678,160 @@ do_exec (gboolean              dont_wait,
     {
       new_argv[ARG_STDERR] = "-";
     }
-
+  
   if (working_directory && *working_directory)
-    new_argv[ARG_WORKING_DIRECTORY] = working_directory;
+    new_argv[ARG_WORKING_DIRECTORY] = protect_argv_string (working_directory);
   else
-    new_argv[ARG_WORKING_DIRECTORY] = "-";
-
-  if (close_descriptors)
+    new_argv[ARG_WORKING_DIRECTORY] = g_strdup ("-");
+  
+  if (!(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN))
     new_argv[ARG_CLOSE_DESCRIPTORS] = "y";
   else
     new_argv[ARG_CLOSE_DESCRIPTORS] = "-";
 
-  if (search_path)
+  if (flags & G_SPAWN_SEARCH_PATH)
     new_argv[ARG_USE_PATH] = "y";
   else
     new_argv[ARG_USE_PATH] = "-";
 
-  if (dont_wait)
+  if (exit_status == NULL)
     new_argv[ARG_WAIT] = "-";
   else
     new_argv[ARG_WAIT] = "w";
 
   for (i = 0; i <= argc; i++)
-    new_argv[ARG_PROGRAM + i] = argv[i];
+    new_argv[ARG_PROGRAM + i] = protected_argv[i];
 
-  /* 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.
-   */
-  if (child_setup)
-    {
-      (* child_setup) (user_data);
-    }
+  SETUP_DEBUG();
 
   if (debug)
     {
-      g_print ("calling gspawn-win32-helper with argv:\n");
+      g_print ("calling %s with argv:\n", helper_process);
       for (i = 0; i < argc + 1 + ARG_COUNT; i++)
        g_print ("argv[%d]: %s\n", i, (new_argv[i] ? new_argv[i] : "NULL"));
     }
-  
-  if (envp != NULL)
-    /* 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);
-  else
-    spawnvp (P_NOWAIT, "gspawn-win32-helper", new_argv);
-
-  /* FIXME: What if gspawn-win32-helper.exe isn't found? */
-
-  /* Close the child_err_report_fd and the other process's ends of the
-   * pipes in this process, otherwise the reader will never get
-   * EOF.
-   */
-  close (child_err_report_fd);
-  if (stdin_fd >= 0)
-    close (stdin_fd);
-  if (stdout_fd >= 0)
-    close (stdout_fd);
-  if (stderr_fd >= 0)
-    close (stderr_fd);
-
-  g_free (new_argv);
-
-  return 0;
-}
 
-static gboolean
-read_ints (int      fd,
-           gint*    buf,
-           gint     n_ints_in_buf,
-           gint    *n_ints_read,
-           GError **error)
-{
-  gint bytes = 0;
-  
-  while (bytes < sizeof(gint)*n_ints_in_buf)
+  if (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
     {
-      gint chunk;
+      if (conv_error_index == ARG_WORKING_DIRECTORY)
+       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR,
+                    _("Invalid working directory: %s"),
+                    conv_error->message);
+      else
+       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                    _("Invalid string in argument vector at %d: %s"),
+                    conv_error_index - ARG_PROGRAM, conv_error->message);
+      g_error_free (conv_error);
+      g_strfreev (protected_argv);
+      g_free (new_argv[0]);
+      g_free (new_argv[ARG_WORKING_DIRECTORY]);
+      g_free (new_argv);
+      g_free (helper_process);
+
+      goto cleanup_and_fail;
+    }
 
-      if (debug)
-       g_print ("%s:read_ints: trying to read %d bytes from pipe...\n",
-                __FILE__,
-                sizeof(gint)*n_ints_in_buf - bytes);
+  if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
+    {
+      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                  _("Invalid string in environment: %s"),
+                  conv_error->message);
+      g_error_free (conv_error);
+      g_strfreev (protected_argv);
+      g_free (new_argv[0]);
+      g_free (new_argv[ARG_WORKING_DIRECTORY]);
+      g_free (new_argv);
+      g_free (helper_process);
+      g_strfreev ((gchar **) wargv);
+      goto cleanup_and_fail;
+    }
 
-      chunk = read (fd, ((gchar*)buf) + bytes,
-                   sizeof(gint)*n_ints_in_buf - bytes);
+  whelper = g_utf8_to_utf16 (helper_process, -1, NULL, NULL, NULL);
+  g_free (helper_process);
 
-      if (debug)
-       g_print ("... got %d bytes\n", chunk);
-          
-      if (chunk < 0)
-        {
-          /* Some weird shit happened, bail out */
-              
-          g_set_error (error,
-                       G_SPAWN_ERROR,
-                       G_SPAWN_ERROR_FAILED,
-                       _("Failed to read from child pipe (%s)"),
-                       g_strerror (errno));
+  if (wenvp != NULL)
+    rc = _wspawnvpe (P_NOWAIT, whelper, (const wchar_t **) wargv, (const wchar_t **) wenvp);
+  else
+    rc = _wspawnvp (P_NOWAIT, whelper, (const wchar_t **) wargv);
 
-          return FALSE;
-        }
-      else if (chunk == 0)
-        break; /* EOF */
-      else
-       bytes += chunk;
-    }
+  saved_errno = errno;
 
-  *n_ints_read = bytes/sizeof(gint);
+  g_free (whelper);
+  g_strfreev ((gchar **) wargv);
+  g_strfreev ((gchar **) wenvp);
 
-  return TRUE;
-}
+  /* Close the other process's ends of the pipes in this process,
+   * otherwise the reader will never get EOF.
+   */
+  close_and_invalidate (&child_err_report_pipe[1]);
+  close_and_invalidate (&helper_sync_pipe[0]);
+  close_and_invalidate (&stdin_pipe[0]);
+  close_and_invalidate (&stdout_pipe[1]);
+  close_and_invalidate (&stderr_pipe[1]);
 
-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)     
-{
-  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 buf[2];
-  gint n_ints = 0;
-  
-  if (!make_pipe (child_err_report_pipe, error))
-    return FALSE;
+  g_strfreev (protected_argv);
 
-  if (standard_input && !make_pipe (stdin_pipe, error))
-    goto cleanup_and_fail;
-  
-  if (standard_output && !make_pipe (stdout_pipe, error))
-    goto cleanup_and_fail;
+  g_free (new_argv[0]);
+  g_free (new_argv[ARG_WORKING_DIRECTORY]);
+  g_free (new_argv);
 
-  if (standard_error && !make_pipe (stderr_pipe, error))
-    goto cleanup_and_fail;
+  /* Check if gspawn-win32-helper couldn't be run */
+  if (rc == -1 && saved_errno != 0)
+    {
+      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                  _("Failed to execute helper program (%s)"),
+                  g_strerror (saved_errno));
+      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);
-      
-  if (!read_ints (child_err_report_pipe[0],
-                 buf, 2, &n_ints,
-                 error))
-    goto cleanup_and_fail;
-        
-  if (n_ints == 2)
+  if (exit_status != NULL)
     {
-      /* Error from the child. */
-      
-      switch (buf[0])
+      /* Synchronous case. Pass helper's report pipe back to caller,
+       * which takes care of reading it after the grandchild has
+       * finished.
+       */
+      g_assert (err_report != NULL);
+      *err_report = child_err_report_pipe[0];
+      write (helper_sync_pipe[1], " ", 1);
+      close_and_invalidate (&helper_sync_pipe[1]);
+    }
+  else
+    {
+      /* Asynchronous case. We read the helper's report right away. */
+      if (!read_helper_report (child_err_report_pipe[0], helper_report, error))
+       goto cleanup_and_fail;
+        
+      close_and_invalidate (&child_err_report_pipe[0]);
+
+      switch (helper_report[0])
        {
        case CHILD_NO_ERROR:
+         if (child_handle && do_return_handle)
+           {
+             /* rc 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) rc, (HANDLE) helper_report[1],
+                                   GetCurrentProcess (), (LPHANDLE) child_handle,
+                                   0, TRUE, DUPLICATE_SAME_ACCESS))
+               {
+                 char *emsg = g_win32_error_message (GetLastError ());
+                 g_print("%s\n", emsg);
+                 *child_handle = 0;
+               }
+           }
+         else if (child_handle)
+           *child_handle = 0;
+         write (helper_sync_pipe[1], " ", 1);
+         close_and_invalidate (&helper_sync_pipe[1]);
          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]));
+       default:
+         write (helper_sync_pipe[1], " ", 1);
+         close_and_invalidate (&helper_sync_pipe[1]);
+         set_child_error (helper_report, working_directory, error);
          goto cleanup_and_fail;
        }
     }
@@ -949,243 +844,669 @@ fork_exec_with_pipes (gboolean              dont_wait,
     *standard_output = stdout_pipe[0];
   if (standard_error)
     *standard_error = stderr_pipe[0];
-  if (exit_status)
-    *exit_status = status;
+  if (rc != -1)
+    CloseHandle ((HANDLE) rc);
   
   return TRUE;
 
  cleanup_and_fail:
-  close_and_invalidate (&child_err_report_pipe[0]);
-  close_and_invalidate (&child_err_report_pipe[1]);
-  close_and_invalidate (&stdin_pipe[0]);
-  close_and_invalidate (&stdin_pipe[1]);
-  close_and_invalidate (&stdout_pipe[0]);
-  close_and_invalidate (&stdout_pipe[1]);
-  close_and_invalidate (&stderr_pipe[0]);
-  close_and_invalidate (&stderr_pipe[1]);
 
-  return FALSE;
-}
+  if (rc != -1)
+    CloseHandle ((HANDLE) rc);
+  if (child_err_report_pipe[0] != -1)
+    close (child_err_report_pipe[0]);
+  if (child_err_report_pipe[1] != -1)
+    close (child_err_report_pipe[1]);
+  if (helper_sync_pipe[0] != -1)
+    close (helper_sync_pipe[0]);
+  if (helper_sync_pipe[1] != -1)
+    close (helper_sync_pipe[1]);
+  if (stdin_pipe[0] != -1)
+    close (stdin_pipe[0]);
+  if (stdin_pipe[1] != -1)
+    close (stdin_pipe[1]);
+  if (stdout_pipe[0] != -1)
+    close (stdout_pipe[0]);
+  if (stdout_pipe[1] != -1)
+    close (stdout_pipe[1]);
+  if (stderr_pipe[0] != -1)
+    close (stderr_pipe[0]);
+  if (stderr_pipe[1] != -1)
+    close (stderr_pipe[1]);
 
-static gboolean
-make_pipe (gint     p[2],
-           GError **error)
-{
-  if (pipe (p) < 0)
-    {
-      g_set_error (error,
-                   G_SPAWN_ERROR,
-                   G_SPAWN_ERROR_FAILED,
-                   _("Failed to create pipe for communicating with child process (%s)"),
-                   g_strerror (errno));
-      return FALSE;
-    }
-  else
-    return TRUE;
+  return FALSE;
 }
 
-#else /* GSPAWN_HELPER */
-
-static void
-write_err_and_exit (gint fd,
-                   gint msg)
+gboolean
+g_spawn_sync_utf8 (const gchar          *working_directory,
+                  gchar               **argv,
+                  gchar               **envp,
+                  GSpawnFlags           flags,
+                  GSpawnChildSetupFunc  child_setup,
+                  gpointer              user_data,
+                  gchar               **standard_output,
+                  gchar               **standard_error,
+                  gint                 *exit_status,
+                  GError              **error)     
 {
-  gint en = errno;
+  gint outpipe = -1;
+  gint errpipe = -1;
+  gint reportpipe = -1;
+  GIOChannel *outchannel = NULL;
+  GIOChannel *errchannel = NULL;
+  GPollFD outfd, errfd;
+  GPollFD fds[2];
+  gint nfds;
+  gint outindex = -1;
+  gint errindex = -1;
+  gint ret;
+  GString *outstr = NULL;
+  GString *errstr = NULL;
+  gboolean failed;
+  gint status;
   
-  write (fd, &msg, sizeof(msg));
-  write (fd, &en, sizeof(en));
+  g_return_val_if_fail (argv != NULL, FALSE);
+  g_return_val_if_fail (!(flags & G_SPAWN_DO_NOT_REAP_CHILD), FALSE);
+  g_return_val_if_fail (standard_output == NULL ||
+                        !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
+  g_return_val_if_fail (standard_error == NULL ||
+                        !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
   
-  _exit (1);
-}
+  /* Just to ensure segfaults if callers try to use
+   * these when an error is reported.
+   */
+  if (standard_output)
+    *standard_output = NULL;
 
-static void
-write_no_error (gint fd)
-{
-  gint msg = CHILD_NO_ERROR;
-  gint en = 0;
+  if (standard_error)
+    *standard_error = NULL;
+  
+  if (!do_spawn_with_pipes (&status,
+                           FALSE,
+                           working_directory,
+                           argv,
+                           envp,
+                           flags,
+                           child_setup,
+                           NULL,
+                           NULL,
+                           standard_output ? &outpipe : NULL,
+                           standard_error ? &errpipe : NULL,
+                           &reportpipe,
+                           error))
+    return FALSE;
 
-  write (fd, &msg, sizeof(msg));
-  write (fd, &en, sizeof(en));
-}
+  /* Read data from child. */
+  
+  failed = FALSE;
 
-#ifdef __GNUC__
-#  ifndef _stdcall
-#    define _stdcall  __attribute__((stdcall))
-#  endif
-#endif
+  if (outpipe >= 0)
+    {
+      outstr = g_string_new (NULL);
+      outchannel = g_io_channel_win32_new_fd (outpipe);
+      g_io_channel_set_encoding (outchannel, NULL, NULL);
+      g_io_channel_set_buffered (outchannel, FALSE);
+      g_io_channel_win32_make_pollfd (outchannel,
+                                     G_IO_IN | G_IO_ERR | G_IO_HUP,
+                                     &outfd);
+      if (debug)
+       g_print ("outfd=%p\n", (HANDLE) outfd.fd);
+    }
+      
+  if (errpipe >= 0)
+    {
+      errstr = g_string_new (NULL);
+      errchannel = g_io_channel_win32_new_fd (errpipe);
+      g_io_channel_set_encoding (errchannel, NULL, NULL);
+      g_io_channel_set_buffered (errchannel, FALSE);
+      g_io_channel_win32_make_pollfd (errchannel,
+                                     G_IO_IN | G_IO_ERR | G_IO_HUP,
+                                     &errfd);
+      if (debug)
+       g_print ("errfd=%p\n", (HANDLE) errfd.fd);
+    }
 
-/* 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.
- */
+  /* Read data until we get EOF on all pipes. */
+  while (!failed && (outpipe >= 0 || errpipe >= 0))
+    {
+      nfds = 0;
+      if (outpipe >= 0)
+       {
+         fds[nfds] = outfd;
+         outindex = nfds;
+         nfds++;
+       }
+      if (errpipe >= 0)
+       {
+         fds[nfds] = errfd;
+         errindex = nfds;
+         nfds++;
+       }
 
-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;
+      if (debug)
+       g_print ("g_spawn_sync: calling g_io_channel_win32_poll, nfds=%d\n",
+                nfds);
 
-  SETUP_DEBUG();
+      ret = g_io_channel_win32_poll (fds, nfds, -1);
 
-  if (debug)
+      if (ret < 0)
+        {
+          failed = TRUE;
+
+          g_set_error_literal (error, G_SPAWN_ERROR, G_SPAWN_ERROR_READ,
+                               _("Unexpected error in g_io_channel_win32_poll() reading data from a child process"));
+         
+          break;
+        }
+
+      if (outpipe >= 0 && (fds[outindex].revents & G_IO_IN))
+        {
+          switch (read_data (outstr, outchannel, error))
+            {
+            case READ_FAILED:
+             if (debug)
+               g_print ("g_spawn_sync: outchannel: READ_FAILED\n");
+              failed = TRUE;
+              break;
+            case READ_EOF:
+             if (debug)
+               g_print ("g_spawn_sync: outchannel: READ_EOF\n");
+              g_io_channel_unref (outchannel);
+             outchannel = NULL;
+              close_and_invalidate (&outpipe);
+              break;
+            default:
+             if (debug)
+               g_print ("g_spawn_sync: outchannel: OK\n");
+              break;
+            }
+
+          if (failed)
+            break;
+        }
+
+      if (errpipe >= 0 && (fds[errindex].revents & G_IO_IN))
+        {
+          switch (read_data (errstr, errchannel, error))
+            {
+            case READ_FAILED:
+             if (debug)
+               g_print ("g_spawn_sync: errchannel: READ_FAILED\n");
+              failed = TRUE;
+              break;
+            case READ_EOF:
+             if (debug)
+               g_print ("g_spawn_sync: errchannel: READ_EOF\n");
+             g_io_channel_unref (errchannel);
+             errchannel = NULL;
+              close_and_invalidate (&errpipe);
+              break;
+            default:
+             if (debug)
+               g_print ("g_spawn_sync: errchannel: OK\n");
+              break;
+            }
+
+          if (failed)
+            break;
+        }
+    }
+
+  if (reportpipe == -1)
     {
-      debugstring = g_string_new ("");
+      /* No helper process, exit status of actual spawned process
+       * already available.
+       */
+      if (exit_status)
+        *exit_status = status;
+    }
+  else
+    {
+      /* Helper process was involved. Read its report now after the
+       * grandchild has finished.
+       */
+      gintptr helper_report[2];
 
-      g_string_append (debugstring,
-                      g_strdup_printf ("g-spawn-win32-helper: "
-                                       "argc = %d, argv: ",
-                                       __argc));
-      for (i = 0; i < __argc; i++)
+      if (!read_helper_report (reportpipe, helper_report, error))
+       failed = TRUE;
+      else
        {
-         if (i > 0)
-           g_string_append (debugstring, " ");
-         g_string_append (debugstring, __argv[i]);
+         switch (helper_report[0])
+           {
+           case CHILD_NO_ERROR:
+             if (exit_status)
+               *exit_status = helper_report[1];
+             break;
+           default:
+             set_child_error (helper_report, working_directory, error);
+             failed = TRUE;
+             break;
+           }
        }
-      
-      MessageBox (NULL, debugstring->str, "gspawn-win32-helper", 0);
+      close_and_invalidate (&reportpipe);
     }
 
-  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')
+  /* These should only be open still if we had an error.  */
+  
+  if (outchannel != NULL)
+    g_io_channel_unref (outchannel);
+  if (errchannel != NULL)
+    g_io_channel_unref (errchannel);
+  if (outpipe >= 0)
+    close_and_invalidate (&outpipe);
+  if (errpipe >= 0)
+    close_and_invalidate (&errpipe);
+  
+  if (failed)
     {
-      fd = open ("NUL:", O_RDONLY);
-      if (fd != 0)
-       {
-         dup2 (fd, 0);
-         close (fd);
-       }
+      if (outstr)
+        g_string_free (outstr, TRUE);
+      if (errstr)
+        g_string_free (errstr, TRUE);
+
+      return FALSE;
     }
   else
     {
-      fd = atoi (__argv[ARG_STDIN]);
-      if (fd != 0)
-       {
-         dup2 (fd, 0);
-         close (fd);
-       }
+      if (standard_output)        
+        *standard_output = g_string_free (outstr, FALSE);
+
+      if (standard_error)
+        *standard_error = g_string_free (errstr, FALSE);
+
+      return TRUE;
     }
+}
 
-  if (__argv[ARG_STDOUT][0] == '-')
-    ; /* Nothing */
-  else if (__argv[ARG_STDOUT][0] == 'z')
+gboolean
+g_spawn_async_with_pipes_utf8 (const gchar          *working_directory,
+                              gchar               **argv,
+                              gchar               **envp,
+                              GSpawnFlags           flags,
+                              GSpawnChildSetupFunc  child_setup,
+                              gpointer              user_data,
+                              GPid                 *child_handle,
+                              gint                 *standard_input,
+                              gint                 *standard_output,
+                              gint                 *standard_error,
+                              GError              **error)
+{
+  g_return_val_if_fail (argv != NULL, FALSE);
+  g_return_val_if_fail (standard_output == NULL ||
+                        !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
+  g_return_val_if_fail (standard_error == NULL ||
+                        !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
+  /* can't inherit stdin if we have an input pipe. */
+  g_return_val_if_fail (standard_input == NULL ||
+                        !(flags & G_SPAWN_CHILD_INHERITS_STDIN), FALSE);
+  
+  return do_spawn_with_pipes (NULL,
+                             (flags & G_SPAWN_DO_NOT_REAP_CHILD),
+                             working_directory,
+                             argv,
+                             envp,
+                             flags,
+                             child_setup,
+                             child_handle,
+                             standard_input,
+                             standard_output,
+                             standard_error,
+                             NULL,
+                             error);
+}
+
+gboolean
+g_spawn_command_line_sync_utf8 (const gchar  *command_line,
+                               gchar       **standard_output,
+                               gchar       **standard_error,
+                               gint         *exit_status,
+                               GError      **error)
+{
+  gboolean retval;
+  gchar **argv = 0;
+
+  g_return_val_if_fail (command_line != NULL, FALSE);
+  
+  if (!g_shell_parse_argv (command_line,
+                           NULL, &argv,
+                           error))
+    return FALSE;
+  
+  retval = g_spawn_sync_utf8 (NULL,
+                             argv,
+                             NULL,
+                             G_SPAWN_SEARCH_PATH,
+                             NULL,
+                             NULL,
+                             standard_output,
+                             standard_error,
+                             exit_status,
+                             error);
+  g_strfreev (argv);
+
+  return retval;
+}
+
+gboolean
+g_spawn_command_line_async_utf8 (const gchar *command_line,
+                                GError     **error)
+{
+  gboolean retval;
+  gchar **argv = 0;
+
+  g_return_val_if_fail (command_line != NULL, FALSE);
+
+  if (!g_shell_parse_argv (command_line,
+                           NULL, &argv,
+                           error))
+    return FALSE;
+  
+  retval = g_spawn_async_utf8 (NULL,
+                              argv,
+                              NULL,
+                              G_SPAWN_SEARCH_PATH,
+                              NULL,
+                              NULL,
+                              NULL,
+                              error);
+  g_strfreev (argv);
+
+  return retval;
+}
+
+void
+g_spawn_close_pid (GPid pid)
+{
+    CloseHandle (pid);
+}
+
+gboolean
+g_spawn_check_exit_status (gint      exit_status,
+                          GError  **error)
+{
+  gboolean ret = FALSE;
+
+  if (exit_status != 0)
     {
-      fd = open ("NUL:", O_WRONLY);
-      if (fd != 1)
-       {
-         dup2 (fd, 1);
-         close (fd);
-       }
+      g_set_error (error, G_SPAWN_EXIT_ERROR, exit_status,
+                  _("Child process exited with code %ld"),
+                  (long) exit_status);
+      goto out;
     }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+#if !defined (_WIN64)
+
+/* Binary compatibility versions that take system codepage pathnames,
+ * argument vectors and environments. These get used only by code
+ * built against 2.8.1 or earlier. Code built against 2.8.2 or later
+ * will use the _utf8 versions above (see the #defines in gspawn.h).
+ */
+
+#undef g_spawn_async
+#undef g_spawn_async_with_pipes
+#undef g_spawn_sync
+#undef g_spawn_command_line_sync
+#undef g_spawn_command_line_async
+
+static gboolean
+setup_utf8_copies (const gchar *working_directory,
+                  gchar      **utf8_working_directory,
+                  gchar      **argv,
+                  gchar     ***utf8_argv,
+                  gchar      **envp,
+                  gchar     ***utf8_envp,
+                  GError     **error)
+{
+  gint i, argc, envc;
+
+  if (working_directory == NULL)
+    *utf8_working_directory = NULL;
   else
     {
-      fd = atoi (__argv[ARG_STDOUT]);
-      if (fd != 1)
+      GError *conv_error = NULL;
+      
+      *utf8_working_directory = g_locale_to_utf8 (working_directory, -1, NULL, NULL, &conv_error);
+      if (*utf8_working_directory == NULL)
        {
-         dup2 (fd, 1);
-         close (fd);
+         g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR,
+                      _("Invalid working directory: %s"),
+                      conv_error->message);
+         g_error_free (conv_error);
+         return FALSE;
        }
     }
 
-  if (__argv[ARG_STDERR][0] == '-')
-    ; /* Nothing */
-  else if (__argv[ARG_STDERR][0] == 'z')
+  argc = 0;
+  while (argv[argc])
+    ++argc;
+  *utf8_argv = g_new (gchar *, argc + 1);
+  for (i = 0; i < argc; i++)
     {
-      fd = open ("NUL:", O_WRONLY);
-      if (fd != 2)
+      GError *conv_error = NULL;
+
+      (*utf8_argv)[i] = g_locale_to_utf8 (argv[i], -1, NULL, NULL, &conv_error);
+      if ((*utf8_argv)[i] == NULL)
        {
-         dup2 (fd, 2);
-         close (fd);
+         g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                      _("Invalid string in argument vector at %d: %s"),
+                      i, conv_error->message);
+         g_error_free (conv_error);
+         
+         g_strfreev (*utf8_argv);
+         *utf8_argv = NULL;
+
+         g_free (*utf8_working_directory);
+         *utf8_working_directory = NULL;
+
+         return FALSE;
        }
     }
+  (*utf8_argv)[argc] = NULL;
+
+  if (envp == NULL)
+    {
+      *utf8_envp = NULL;
+    }
   else
     {
-      fd = atoi (__argv[ARG_STDERR]);
-      if (fd != 2)
+      envc = 0;
+      while (envp[envc])
+       ++envc;
+      *utf8_envp = g_new (gchar *, envc + 1);
+      for (i = 0; i < envc; i++)
        {
-         dup2 (fd, 2);
-         close (fd);
+         GError *conv_error = NULL;
+
+         (*utf8_envp)[i] = g_locale_to_utf8 (envp[i], -1, NULL, NULL, &conv_error);
+         if ((*utf8_envp)[i] == NULL)
+           {   
+             g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                          _("Invalid string in environment: %s"),
+                          conv_error->message);
+             g_error_free (conv_error);
+
+             g_strfreev (*utf8_envp);
+             *utf8_envp = NULL;
+
+             g_strfreev (*utf8_argv);
+             *utf8_argv = NULL;
+
+             g_free (*utf8_working_directory);
+             *utf8_working_directory = NULL;
+
+             return FALSE;
+           }
        }
+      (*utf8_envp)[envc] = NULL;
     }
+  return TRUE;
+}
 
-  /* __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
-   */
+static void
+free_utf8_copies (gchar  *utf8_working_directory,
+                 gchar **utf8_argv,
+                 gchar **utf8_envp)
+{
+  g_free (utf8_working_directory);
+  g_strfreev (utf8_argv);
+  g_strfreev (utf8_envp);
+}
 
-  if (__argv[ARG_CLOSE_DESCRIPTORS][0] == 'y')
-    for (i = 3; i < 1000; i++) /* FIXME real limit? */
-      if (i != child_err_report_fd)
-       close (i);
+gboolean
+g_spawn_async_with_pipes (const gchar          *working_directory,
+                          gchar               **argv,
+                          gchar               **envp,
+                          GSpawnFlags           flags,
+                          GSpawnChildSetupFunc  child_setup,
+                          gpointer              user_data,
+                          GPid                 *child_handle,
+                          gint                 *standard_input,
+                          gint                 *standard_output,
+                          gint                 *standard_error,
+                          GError              **error)
+{
+  gchar *utf8_working_directory;
+  gchar **utf8_argv;
+  gchar **utf8_envp;
+  gboolean retval;
 
-  /* __argv[ARG_WAIT] is "w" to wait for the program to exit */
+  if (!setup_utf8_copies (working_directory, &utf8_working_directory,
+                         argv, &utf8_argv,
+                         envp, &utf8_envp,
+                         error))
+    return FALSE;
 
-  if (__argv[ARG_WAIT][0] == 'w')
-    mode = P_WAIT;
-  else
-    mode = P_NOWAIT;
+  retval = g_spawn_async_with_pipes_utf8 (utf8_working_directory,
+                                         utf8_argv, utf8_envp,
+                                         flags, child_setup, user_data,
+                                         child_handle,
+                                         standard_input, standard_output, standard_error,
+                                         error);
 
-  /* __argv[ARG_USE_PATH] is "y" to use PATH, otherwise not */
+  free_utf8_copies (utf8_working_directory, utf8_argv, utf8_envp);
 
-  /* __argv[ARG_PROGRAM] is program file to run,
-   * __argv[ARG_PROGRAM+1]... is its __argv.
-   */
+  return retval;
+}
 
-  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);
-    }
+gboolean
+g_spawn_async (const gchar          *working_directory,
+              gchar               **argv,
+              gchar               **envp,
+              GSpawnFlags           flags,
+              GSpawnChildSetupFunc  child_setup,
+              gpointer              user_data,
+              GPid                 *child_handle,
+              GError              **error)
+{
+  return g_spawn_async_with_pipes (working_directory,
+                                  argv, envp,
+                                  flags,
+                                  child_setup,
+                                  user_data,
+                                  child_handle,
+                                  NULL, NULL, NULL,
+                                  error);
+}
 
-  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);
-    }
+gboolean
+g_spawn_sync (const gchar          *working_directory,
+             gchar               **argv,
+             gchar               **envp,
+             GSpawnFlags           flags,
+             GSpawnChildSetupFunc  child_setup,
+             gpointer              user_data,
+             gchar               **standard_output,
+             gchar               **standard_error,
+             gint                 *exit_status,
+             GError              **error)     
+{
+  gchar *utf8_working_directory;
+  gchar **utf8_argv;
+  gchar **utf8_envp;
+  gboolean retval;
+
+  if (!setup_utf8_copies (working_directory, &utf8_working_directory,
+                         argv, &utf8_argv,
+                         envp, &utf8_envp,
+                         error))
+    return FALSE;
+
+  retval = g_spawn_sync_utf8 (utf8_working_directory,
+                             utf8_argv, utf8_envp,
+                             flags, child_setup, user_data,
+                             standard_output, standard_error, exit_status,
+                             error);
+
+  free_utf8_copies (utf8_working_directory, utf8_argv, utf8_envp);
 
-  return 0;
+  return retval;
 }
 
-#endif /* GSPAWN_HELPER */
+gboolean
+g_spawn_command_line_sync (const gchar  *command_line,
+                          gchar       **standard_output,
+                          gchar       **standard_error,
+                          gint         *exit_status,
+                          GError      **error)
+{
+  gboolean retval;
+  gchar **argv = 0;
+
+  g_return_val_if_fail (command_line != NULL, FALSE);
+  
+  if (!g_shell_parse_argv (command_line,
+                           NULL, &argv,
+                           error))
+    return FALSE;
+  
+  retval = g_spawn_sync (NULL,
+                         argv,
+                         NULL,
+                         G_SPAWN_SEARCH_PATH,
+                         NULL,
+                         NULL,
+                         standard_output,
+                         standard_error,
+                         exit_status,
+                         error);
+  g_strfreev (argv);
+
+  return retval;
+}
+
+gboolean
+g_spawn_command_line_async (const gchar *command_line,
+                           GError     **error)
+{
+  gboolean retval;
+  gchar **argv = 0;
+
+  g_return_val_if_fail (command_line != NULL, FALSE);
+
+  if (!g_shell_parse_argv (command_line,
+                           NULL, &argv,
+                           error))
+    return FALSE;
+  
+  retval = g_spawn_async (NULL,
+                          argv,
+                          NULL,
+                          G_SPAWN_SEARCH_PATH,
+                          NULL,
+                          NULL,
+                          NULL,
+                          error);
+  g_strfreev (argv);
+
+  return retval;
+}
+
+#endif /* !_WIN64 */
+
+#endif /* !GSPAWN_HELPER */