From: theadib Date: Thu, 27 Mar 2008 21:47:23 +0000 (+0000) Subject: Bug #204779 win32 crash on help menu - win32 spawn problem X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=1121018f2328f23625a4f79b636d83ae340190c2;p=inkscape.git Bug #204779 win32 crash on help menu - win32 spawn problem --- diff --git a/src/extension/implementation/script.cpp b/src/extension/implementation/script.cpp index da60c8821..69f8cec53 100644 --- a/src/extension/implementation/script.cpp +++ b/src/extension/implementation/script.cpp @@ -1029,7 +1029,7 @@ Script::execute (const std::list &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(), diff --git a/src/io/sys.cpp b/src/io/sys.cpp index 2f1b92b40..e659774e2 100644 --- a/src/io/sys.cpp +++ b/src/io/sys.cpp @@ -28,11 +28,24 @@ #ifdef WIN32 +#define BYPASS_GLIB_SPAWN 1 + +#ifdef BYPASS_GLIB_SPAWN + +#include // declares spawn functions +#include // declares _wspawn functions +#include +#include +#include +#include + +#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*>(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& argv, + GSpawnFlags flags, + const sigc::slot& child_setup, + Pid* child_pid, + int* standard_input, + int* standard_output, + int* standard_error) +{ + const bool setup_slot = !child_setup.empty(); + sigc::slot child_setup_ = child_setup; + GError* error = 0; + + my_spawn_async_with_pipes_utf8( + working_directory.c_str(), + const_cast(argv.data()), 0, + static_cast(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& argv, + Glib::SpawnFlags flags, + const sigc::slot& 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(flags), + child_setup, + child_pid, + standard_input, + standard_output, + standard_error); +#endif +} + gchar* Inkscape::IO::sanitizeString( gchar const * str ) { diff --git a/src/io/sys.h b/src/io/sys.h index b31602d91..29c33c1c7 100644 --- a/src/io/sys.h +++ b/src/io/sys.h @@ -18,6 +18,7 @@ #include #include #include +#include #include /*##################### @@ -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& argv, + Glib::SpawnFlags flags, + const sigc::slot& child_setup, + Glib::Pid* child_pid, + int* standard_input, + int* standard_output, + int* standard_error); + } }