Code

Bug #204779 win32 crash on help menu - win32 spawn problem
authortheadib <theadib@users.sourceforge.net>
Thu, 27 Mar 2008 21:47:23 +0000 (21:47 +0000)
committertheadib <theadib@users.sourceforge.net>
Thu, 27 Mar 2008 21:47:23 +0000 (21:47 +0000)
src/extension/implementation/script.cpp
src/io/sys.cpp
src/io/sys.h

index da60c8821c6d21aea20884a3c920d4717d62a2e2..69f8cec533db017e52bddfbdb011c4888dfdf29b 100644 (file)
@@ -1029,7 +1029,7 @@ Script::execute (const std::list<std::string> &in_command,
     int stdout_pipe, stderr_pipe;
 
     try {
-        Glib::spawn_async_with_pipes(Glib::get_current_dir(), // working directory
+        Inkscape::IO::spawn_async_with_pipes(Glib::get_current_dir(), // working directory
                                      argv,  // arg v
                                      Glib::SPAWN_SEARCH_PATH /*| Glib::SPAWN_DO_NOT_REAP_CHILD*/,
                                      sigc::slot<void>(),
index 2f1b92b40280cd3ac76e83f700b7ee8c3d9c33e8..e659774e2332b41d0b54d613213f43c00f77a442 100644 (file)
 
 #ifdef WIN32
 
+#define BYPASS_GLIB_SPAWN 1
+
+#ifdef BYPASS_GLIB_SPAWN
+
+#include <process.h>   // declares spawn functions
+#include <wchar.h>     // declares _wspawn functions
+#include <unistd.h>
+#include <glibmm/i18n.h>
+#include <fcntl.h>
+#include <io.h>
+
+#endif // BYPASS_GLIB_SPAWN
+
 // For now to get at is_os_wide().
 #include "extension/internal/win32.h"
 using Inkscape::Extension::Internal::PrintWin32;
 
-#endif
+#endif // WIN32
 
 //#define INK_DUMP_FILENAME_CONV 1
 #undef INK_DUMP_FILENAME_CONV
@@ -307,6 +320,874 @@ gchar* Inkscape::IO::locale_to_utf8_fallback( const gchar *opsysstring,
     return result;
 }
 
+#ifdef BYPASS_GLIB_SPAWN
+/*
+       this code was taken from the original glib sources
+*/
+#define GSPAWN_HELPER
+enum
+{
+  CHILD_NO_ERROR,
+  CHILD_CHDIR_FAILED,
+  CHILD_SPAWN_FAILED,
+};
+
+enum {
+  ARG_CHILD_ERR_REPORT = 1,
+  ARG_HELPER_SYNC,
+  ARG_STDIN,
+  ARG_STDOUT,
+  ARG_STDERR,
+  ARG_WORKING_DIRECTORY,
+  ARG_CLOSE_DESCRIPTORS,
+  ARG_USE_PATH,
+  ARG_WAIT,
+  ARG_PROGRAM,
+  ARG_COUNT = ARG_PROGRAM
+};
+static int debug = 0;
+#define HELPER_PROCESS "gspawn-win32-helper"
+
+
+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 ((long) filehandle, mode | _O_NOINHERIT);
+}
+
+
+/* The helper process writes a status report back to us, through a
+ * pipe, consisting of two ints.
+ */
+static gboolean
+read_helper_report (int      fd,
+                   gint     report[2],
+                   GError **error)
+{
+  gint bytes = 0;
+
+  while (bytes < sizeof(gint)*2)
+    {
+      gint chunk;
+
+      if (debug)
+       g_print ("%s:read_helper_report: read %d...\n",
+                __FILE__,
+                sizeof(gint)*2 - bytes);
+
+      chunk = read (fd, ((gchar*)report) + bytes,
+                   sizeof(gint)*2 - bytes);
+
+      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));
+
+          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 (bytes < sizeof(gint)*2)
+    return FALSE;
+
+  return TRUE;
+}
+
+
+static void
+set_child_error (gint         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 ();
+    }
+}
+
+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 = (gchar *)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)
+{
+  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;
+}
+
+
+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] = (wchar_t *)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;
+}
+
+
+/* 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 void
+close_and_invalidate (gint *fd)
+{
+  if (*fd < 0)
+    return;
+
+  close (*fd);
+  *fd = -1;
+}
+
+
+static gboolean
+do_spawn_directly (gint                 *exit_status,
+                  gboolean              do_return_handle,
+                  GSpawnFlags           flags,
+                  gchar               **argv,
+                  char                **envp,
+                  char                **protected_argv,
+                  GSpawnChildSetupFunc  child_setup,
+                  gpointer              user_data,
+                  GPid                 *child_handle,
+                  GError              **error)
+{
+  const int mode = (exit_status == NULL) ? P_NOWAIT : P_WAIT;
+  char **new_argv;
+  int 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 = (wchar_t *)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 (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
+    {
+      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;
+    }
+
+  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_free (wargv0);
+      g_strfreev ((gchar **) wargv);
+
+      return FALSE;
+    }
+
+  if (child_setup)
+    (* child_setup) (user_data);
+
+  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;
+    }
+
+  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 TRUE;
+}
+
+static gboolean
+make_pipe (gint     p[2],
+           GError **error)
+{
+  if (_pipe (p, 4096, _O_BINARY) < 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;
+}
+
+
+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,
+                    gpointer              user_data,
+                    GPid                 *child_handle,
+                    gint                 *standard_input,
+                    gint                 *standard_output,
+                    gint                 *standard_error,
+                    gint                 *err_report,
+                    GError              **error)
+{
+  char **protected_argv;
+  char args[ARG_COUNT][10];
+  char **new_argv;
+  int i;
+  int 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 };
+  int 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;
+  //extern gchar *_glib_get_installation_directory (void);
+  gchar *glib_top;
+
+  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 and dangerous on Win32");
+    }
+
+  argc = protect_argv (argv, &protected_argv);
+
+  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_setup, user_data, child_handle,
+                          error);
+      g_strfreev (protected_argv);
+      return retval;
+    }
+
+  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 (GetConsoleCursorInfo (GetStdHandle (STD_OUTPUT_HANDLE), &cursor_info))
+    helper_process = HELPER_PROCESS "-console.exe";
+  else
+    helper_process = HELPER_PROCESS ".exe";
+
+  glib_top = NULL;
+  if (glib_top != NULL)
+    {
+      helper_process = g_build_filename (glib_top, "bin", helper_process, NULL);
+      g_free (glib_top);
+    }
+  else
+    helper_process = g_strdup (helper_process);
+
+  new_argv[0] = protect_argv_string (helper_process);
+
+  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], "#");
+    }
+
+  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 (standard_input)
+    {
+      sprintf (args[ARG_STDIN], "%d", stdin_pipe[0]);
+      new_argv[ARG_STDIN] = args[ARG_STDIN];
+    }
+  else if (flags & G_SPAWN_CHILD_INHERITS_STDIN)
+    {
+      /* Let stdin be alone */
+      new_argv[ARG_STDIN] = "-";
+    }
+  else
+    {
+      /* Keep process from blocking on a read of stdin */
+      new_argv[ARG_STDIN] = "z";
+    }
+
+  if (standard_output)
+    {
+      sprintf (args[ARG_STDOUT], "%d", stdout_pipe[1]);
+      new_argv[ARG_STDOUT] = args[ARG_STDOUT];
+    }
+  else if (flags & G_SPAWN_STDOUT_TO_DEV_NULL)
+    {
+      new_argv[ARG_STDOUT] = "z";
+    }
+  else
+    {
+      new_argv[ARG_STDOUT] = "-";
+    }
+
+  if (standard_error)
+    {
+      sprintf (args[ARG_STDERR], "%d", stderr_pipe[1]);
+     new_argv[ARG_STDERR] = args[ARG_STDERR];
+    }
+  else if (flags & G_SPAWN_STDERR_TO_DEV_NULL)
+    {
+      new_argv[ARG_STDERR] = "z";
+    }
+  else
+    {
+      new_argv[ARG_STDERR] = "-";
+    }
+
+  if (working_directory && *working_directory)
+    new_argv[ARG_WORKING_DIRECTORY] = protect_argv_string (working_directory);
+  else
+    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 (flags & G_SPAWN_SEARCH_PATH)
+    new_argv[ARG_USE_PATH] = "y";
+  else
+    new_argv[ARG_USE_PATH] = "-";
+
+  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] = protected_argv[i];
+
+  //SETUP_DEBUG();
+
+  if (debug)
+    {
+      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 (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
+    {
+      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 (!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;
+    }
+
+  if (child_setup)
+    (* child_setup) (user_data);
+
+  whelper = (wchar_t *)g_utf8_to_utf16 (helper_process, -1, NULL, NULL, NULL);
+  g_free (helper_process);
+
+  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);
+
+  saved_errno = errno;
+
+  g_free (whelper);
+  g_strfreev ((gchar **) wargv);
+  g_strfreev ((gchar **) wenvp);
+
+  /* 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]);
+
+  g_strfreev (protected_argv);
+
+  g_free (new_argv[0]);
+  g_free (new_argv[ARG_WORKING_DIRECTORY]);
+  g_free (new_argv);
+
+  /* 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;
+    }
+
+  if (exit_status != NULL)
+    {
+      /* 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;
+
+       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;
+       }
+    }
+
+  /* Success against all odds! return the information */
+
+  if (standard_input)
+    *standard_input = stdin_pipe[1];
+  if (standard_output)
+    *standard_output = stdout_pipe[0];
+  if (standard_error)
+    *standard_error = stderr_pipe[0];
+  if (rc != -1)
+    CloseHandle ((HANDLE) rc);
+
+  return TRUE;
+
+  cleanup_and_fail:
+
+  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]);
+
+  return FALSE;
+}
+
+gboolean
+my_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,
+                             user_data,
+                             child_handle,
+                             standard_input,
+                             standard_output,
+                             standard_error,
+                             NULL,
+                             error);
+}
+
+typedef GPid Pid;
+
+// _WRAP_ENUM(SpawnFlags, GSpawnFlags, NO_GTYPE)
+
+/* Helper callback to invoke the actual sigc++ slot.
+ * We don't need to worry about (un)referencing, since the
+ * child process gets its own copy of the parent's memory anyway.
+ */
+static void child_setup_callback(void* user_data)
+{
+  #ifdef GLIBMM_EXCEPTIONS_ENABLED
+  try
+  {
+  #endif //GLIBMM_EXCEPTIONS_ENABLED
+    (*reinterpret_cast<sigc::slot<void>*>(user_data))();
+  #ifdef GLIBMM_EXCEPTIONS_ENABLED
+  }
+  catch(...)
+  {
+    Glib::exception_handlers_invoke();
+  }
+  #endif //GLIBMM_EXCEPTIONS_ENABLED
+}
+
+
+void my_spawn_async_with_pipes(const std::string& working_directory,
+                            const Glib::ArrayHandle<std::string>& argv,
+                            GSpawnFlags flags,
+                            const sigc::slot<void>& child_setup,
+                            Pid* child_pid,
+                            int* standard_input,
+                            int* standard_output,
+                            int* standard_error)
+{
+  const bool setup_slot = !child_setup.empty();
+  sigc::slot<void> child_setup_ = child_setup;
+  GError* error = 0;
+
+  my_spawn_async_with_pipes_utf8(
+      working_directory.c_str(),
+      const_cast<char**>(argv.data()), 0,
+      static_cast<GSpawnFlags>(unsigned(flags)),
+      (setup_slot) ? &child_setup_callback : 0,
+      (setup_slot) ? &child_setup_         : 0,
+      child_pid,
+      standard_input, standard_output, standard_error,
+      &error);
+
+  if(error)
+    Glib::Error::throw_exception(error);
+}
+
+#endif
+
+void
+Inkscape::IO::spawn_async_with_pipes( const std::string& working_directory,
+                                      const Glib::ArrayHandle<std::string>& argv,
+                                      Glib::SpawnFlags flags,
+                                      const sigc::slot<void>& child_setup,
+                                      Glib::Pid* child_pid,
+                                      int* standard_input,
+                                      int* standard_output,
+                                      int* standard_error)
+{
+#ifndef BYPASS_GLIB_SPAWN
+    Glib::spawn_async_with_pipes(working_directory,
+                                 argv,
+                                 flags,
+                                 child_setup,
+                                 child_pid,
+                                 standard_input,
+                                 standard_output,
+                                 standard_error);
+#else
+    my_spawn_async_with_pipes(working_directory,
+                              argv,
+                              static_cast<GSpawnFlags>(flags),
+                              child_setup,
+                              child_pid,
+                              standard_input,
+                              standard_output,
+                              standard_error);
+#endif
+}
+
 
 gchar* Inkscape::IO::sanitizeString( gchar const * str )
 {
index b31602d9129bc5df57fe4135bbc4474f54052359..29c33c1c7c24919829e0923f19b23c028af46ce0 100644 (file)
@@ -18,6 +18,7 @@
 #include <glib/gtypes.h>
 #include <glib/gdir.h>
 #include <glib/gfileutils.h>
+#include <glibmm/spawn.h>
 #include <string>
 
 /*#####################
@@ -49,6 +50,15 @@ gchar* locale_to_utf8_fallback( const gchar *opsysstring,
 
 gchar* sanitizeString( gchar const * str );
 
+void spawn_async_with_pipes (const std::string& working_directory,
+                             const Glib::ArrayHandle<std::string>& argv,
+                             Glib::SpawnFlags flags,
+                             const sigc::slot<void>& child_setup,
+                             Glib::Pid* child_pid,
+                             int* standard_input,
+                             int* standard_output,
+                             int* standard_error);
+
 }
 }