+2005-08-25 Tor Lillqvist <tml@novell.com>
+
+ Make also the g_spawn*() functions take parameters in the GLib
+ file name encoding, i.e. UTF-8, on Windows. Has no impact on Unix
+ API or ABI. Like the other GLib API that was earlier changed to
+ use UTF-8 on Windows, the names of the functions that take UTF-8
+ have _utf8 suffixes added by using preprocessor macros in the
+ header file. The old names are kept for functions with the old
+ behaviour, taking parameters in the system codepage, for DLL ABI
+ stability.
+
+ * glib/gspawn.h: On Win32 add the suffix _utf8 to the names of the
+ g_spawn*() functions.
+
+ * glib/gspawn-win32.c: Use wide-char API on NT-based
+ Windows. Convert parameters from UTF-8 to wide chars (NT) or
+ system codepage (Win9x) and call the C library _wspawn*() or
+ spawn*() functions respectvely. Add DLL ABI stability versions
+ that take parameters in the system codepage.
+
+ * glib/gspawn-win32-helper.c: On NT-based Windows use the
+ wide-char versions of argv and envp, and use wide-char API to
+ change directory and spawn the program to run. Remove the verbose
+ debugging output, it was too complex to modify for the wide-char
+ features. (Just add temporary debugging printouts if needed, no
+ need to have them permanently in the source.)
+
+ * glib/gspawn.c: Corresponding documentation updates.
+
+ * glib/glib.symbols: Corresponding changes: Mark the ABI stability
+ symbols as PRIVATE, add the new _utf8-suffixed ones.
+
2005-08-24 Stepan Kasal <kasal@ucw.cz>
* glib/gtypes.h (G_MININT64): Cast the constant to gint64; it is
+2005-08-25 Tor Lillqvist <tml@novell.com>
+
+ Make also the g_spawn*() functions take parameters in the GLib
+ file name encoding, i.e. UTF-8, on Windows. Has no impact on Unix
+ API or ABI. Like the other GLib API that was earlier changed to
+ use UTF-8 on Windows, the names of the functions that take UTF-8
+ have _utf8 suffixes added by using preprocessor macros in the
+ header file. The old names are kept for functions with the old
+ behaviour, taking parameters in the system codepage, for DLL ABI
+ stability.
+
+ * glib/gspawn.h: On Win32 add the suffix _utf8 to the names of the
+ g_spawn*() functions.
+
+ * glib/gspawn-win32.c: Use wide-char API on NT-based
+ Windows. Convert parameters from UTF-8 to wide chars (NT) or
+ system codepage (Win9x) and call the C library _wspawn*() or
+ spawn*() functions respectvely. Add DLL ABI stability versions
+ that take parameters in the system codepage.
+
+ * glib/gspawn-win32-helper.c: On NT-based Windows use the
+ wide-char versions of argv and envp, and use wide-char API to
+ change directory and spawn the program to run. Remove the verbose
+ debugging output, it was too complex to modify for the wide-char
+ features. (Just add temporary debugging printouts if needed, no
+ need to have them permanently in the source.)
+
+ * glib/gspawn.c: Corresponding documentation updates.
+
+ * glib/glib.symbols: Corresponding changes: Mark the ABI stability
+ symbols as PRIVATE, add the new _utf8-suffixed ones.
+
2005-08-24 Stepan Kasal <kasal@ucw.cz>
* glib/gtypes.h (G_MININT64): Cast the constant to gint64; it is
+2005-08-25 Tor Lillqvist <tml@novell.com>
+
+ Make also the g_spawn*() functions take parameters in the GLib
+ file name encoding, i.e. UTF-8, on Windows. Has no impact on Unix
+ API or ABI. Like the other GLib API that was earlier changed to
+ use UTF-8 on Windows, the names of the functions that take UTF-8
+ have _utf8 suffixes added by using preprocessor macros in the
+ header file. The old names are kept for functions with the old
+ behaviour, taking parameters in the system codepage, for DLL ABI
+ stability.
+
+ * glib/gspawn.h: On Win32 add the suffix _utf8 to the names of the
+ g_spawn*() functions.
+
+ * glib/gspawn-win32.c: Use wide-char API on NT-based
+ Windows. Convert parameters from UTF-8 to wide chars (NT) or
+ system codepage (Win9x) and call the C library _wspawn*() or
+ spawn*() functions respectvely. Add DLL ABI stability versions
+ that take parameters in the system codepage.
+
+ * glib/gspawn-win32-helper.c: On NT-based Windows use the
+ wide-char versions of argv and envp, and use wide-char API to
+ change directory and spawn the program to run. Remove the verbose
+ debugging output, it was too complex to modify for the wide-char
+ features. (Just add temporary debugging printouts if needed, no
+ need to have them permanently in the source.)
+
+ * glib/gspawn.c: Corresponding documentation updates.
+
+ * glib/glib.symbols: Corresponding changes: Mark the ABI stability
+ symbols as PRIVATE, add the new _utf8-suffixed ones.
+
2005-08-24 Stepan Kasal <kasal@ucw.cz>
* glib/gtypes.h (G_MININT64): Cast the constant to gint64; it is
#if IN_HEADER(__G_SPAWN_H__)
#if IN_FILE(__G_SPAWN_C__)
-g_spawn_async
-g_spawn_async_with_pipes
+g_spawn_async PRIVATE
+g_spawn_async_with_pipes PRIVATE
g_spawn_close_pid
-g_spawn_command_line_async
-g_spawn_command_line_sync
+g_spawn_command_line_async PRIVATE
+g_spawn_command_line_sync PRIVATE
g_spawn_error_quark
-g_spawn_sync
+g_spawn_sync PRIVATE
+#ifdef G_OS_WIN32
+g_spawn_async_utf8
+g_spawn_async_with_pipes_utf8
+g_spawn_command_line_async_utf8
+g_spawn_command_line_sync_utf8
+g_spawn_sync_utf8
+#endif
#endif
#endif
#include "gspawn-win32.c" /* For shared definitions */
-static GString *debugstring;
-
static void
write_err_and_exit (gint fd,
gint msg)
{
gint en = errno;
- if (debug)
- {
- debugstring = g_string_new (NULL);
- g_string_append (debugstring,
- g_strdup_printf ("writing error code %d and errno %d",
- msg, en));
- MessageBox (NULL, debugstring->str, "gspawn-win32-helper", 0);
- }
-
write (fd, &msg, sizeof(msg));
write (fd, &en, sizeof(en));
* away in the global __argc and __argv by the C runtime startup code.
*/
+/* Info peeked from mingw runtime's source code. __wgetmainargs() is a
+ * function to get the program's argv in wide char format.
+ */
+
+typedef struct {
+ int newmode;
+} _startupinfo;
+
+extern void __wgetmainargs(int *argc,
+ wchar_t ***wargv,
+ wchar_t ***wenviron,
+ int expand_wildcards,
+ _startupinfo *startupinfo);
+
+/* Copy of protect_argv that handles wchar_t strings */
+
+static gint
+protect_wargv (wchar_t **wargv,
+ wchar_t ***new_wargv)
+{
+ gint i;
+ gint argc = 0;
+
+ while (wargv[argc])
+ ++argc;
+ *new_wargv = g_new (wchar_t *, 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_wargv[0] constructed by this function should
+ * *not* be passed as the filename argument to a _wspawn* or _wexec*
+ * family function. That argument should be the real file name
+ * without any quoting.
+ */
+ for (i = 0; i < argc; i++)
+ {
+ wchar_t *p = wargv[i];
+ wchar_t *q;
+ gint len = 0;
+ gboolean need_dblquotes = FALSE;
+ while (*p)
+ {
+ if (*p == ' ' || *p == '\t')
+ need_dblquotes = TRUE;
+ else if (*p == '"')
+ len++;
+ else if (*p == '\\')
+ {
+ wchar_t *pp = p;
+ while (*pp && *pp == '\\')
+ pp++;
+ if (*pp == '"')
+ len++;
+ }
+ len++;
+ p++;
+ }
+
+ q = (*new_wargv)[i] = g_new (wchar_t, len + need_dblquotes*2 + 1);
+ p = wargv[i];
+
+ if (need_dblquotes)
+ *q++ = '"';
+
+ while (*p)
+ {
+ if (*p == '"')
+ *q++ = '\\';
+ else if (*p == '\\')
+ {
+ wchar_t *pp = p;
+ while (*pp && *pp == '\\')
+ pp++;
+ if (*pp == '"')
+ *q++ = '\\';
+ }
+ *q++ = *p;
+ p++;
+ }
+
+ if (need_dblquotes)
+ *q++ = '"';
+ *q++ = '\0';
+ }
+ (*new_wargv)[argc] = NULL;
+
+ return argc;
+}
+
int _stdcall
WinMain (struct HINSTANCE__ *hInstance,
struct HINSTANCE__ *hPrevInstance,
int saved_errno;
int no_error = CHILD_NO_ERROR;
int zero = 0;
- gint file_and_argv_zero = 0;
+ gint argv_zero_offset = ARG_PROGRAM;
gchar **new_argv;
+ wchar_t **new_wargv;
+ int argc;
+ wchar_t **wargv, **wenvp;
+ _startupinfo si = { 0 };
- SETUP_DEBUG();
+ g_assert (__argc >= ARG_COUNT);
- if (debug)
+ if (G_WIN32_HAVE_WIDECHAR_API ())
{
- debugstring = g_string_new (NULL);
-
- g_string_append (debugstring,
- g_strdup_printf ("g-spawn-win32-helper: "
- "argc = %d, argv: ",
- __argc));
- for (i = 0; i < __argc; i++)
- {
- if (i > 0)
- g_string_append (debugstring, " ");
- g_string_append (debugstring, __argv[i]);
- }
-
- MessageBox (NULL, debugstring->str, "gspawn-win32-helper", 0);
+ /* Fetch the wide-char argument vector */
+ __wgetmainargs (&argc, &wargv, &wenvp, 0, &si);
+
+ /* We still have the system codepage args in __argv. We can look
+ * at the first args in which gspawn-win32.c passes us flags and
+ * fd numbers in __argv, as we know those are just ASCII anyway.
+ */
+ g_assert (argc == __argc);
}
- g_assert (__argc >= ARG_COUNT);
-
- /* argv[ARG_CHILD_ERR_REPORT] is the file descriptor onto which
- * write error messages.
+ /* argv[ARG_CHILD_ERR_REPORT] is the file descriptor number onto
+ * which write error messages.
*/
child_err_report_fd = atoi (__argv[ARG_CHILD_ERR_REPORT]);
- /* Hack to implement G_SPAWN_FILE_AND_ARGV_ZERO */
+ /* Hack to implement G_SPAWN_FILE_AND_ARGV_ZERO. If
+ * argv[ARG_CHILD_ERR_REPORT] is suffixed with a '#' it means we get
+ * the program to run and its argv[0] separately.
+ */
if (__argv[ARG_CHILD_ERR_REPORT][strlen (__argv[ARG_CHILD_ERR_REPORT]) - 1] == '#')
- file_and_argv_zero = 1;
+ argv_zero_offset++;
- /* 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:.
+ /* argv[ARG_STDIN..ARG_STDERR] are the file descriptor numbers that
+ * should be dup2'd to 0, 1 and 2. '-' if the corresponding fd
+ * should be left alone, and 'z' if it should be connected to the
+ * bit bucket NUL:.
*/
if (__argv[ARG_STDIN][0] == '-')
; /* Nothing */
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);
+ else if ((G_WIN32_HAVE_WIDECHAR_API () &&
+ _wchdir (wargv[ARG_WORKING_DIRECTORY]) < 0) ||
+ (!G_WIN32_HAVE_WIDECHAR_API () &&
+ chdir (__argv[ARG_WORKING_DIRECTORY]) < 0))
+ write_err_and_exit (child_err_report_fd, CHILD_CHDIR_FAILED);
/* __argv[ARG_CLOSE_DESCRIPTORS] is "y" if file descriptors from 3
* upwards should be closed
*/
-
if (__argv[ARG_CLOSE_DESCRIPTORS][0] == 'y')
for (i = 3; i < 1000; i++) /* FIXME real limit? */
if (i != child_err_report_fd)
close (i);
/* __argv[ARG_WAIT] is "w" to wait for the program to exit */
-
if (__argv[ARG_WAIT][0] == 'w')
mode = P_WAIT;
else
/* __argv[ARG_USE_PATH] is "y" to use PATH, otherwise not */
- /* __argv[ARG_PROGRAM] is program file to run,
- * __argv[ARG_PROGRAM+1]... is its __argv.
+ /* __argv[ARG_PROGRAM] is executable file to run,
+ * __argv[argv_zero_offset]... is its argv. argv_zero_offset equals
+ * ARG_PROGRAM unless G_SPAWN_FILE_AND_ARGV_ZERO was used, in which
+ * case we have a separate executable name and argv[0].
*/
- protect_argv (__argv, &new_argv);
-
/* For the program name passed to spawnv(), don't use the quoted
- * version. */
-
- if (debug)
+ * version.
+ */
+ if (G_WIN32_HAVE_WIDECHAR_API ())
{
- debugstring = g_string_new (NULL);
- g_string_append (debugstring,
- g_strdup_printf ("calling %s %s mode=%s argv: ",
- (__argv[ARG_USE_PATH][0] == 'y' ?
- "spawnvp" : "spawnv"),
- __argv[ARG_PROGRAM],
- (mode == P_WAIT ?
- "P_WAIT" : "P_NOWAIT")));
- i = ARG_PROGRAM + 1 + file_and_argv_zero;
- while (new_argv[i])
- {
- g_string_append (debugstring, new_argv[i++]);
- if (new_argv[i])
- g_string_append (debugstring, " ");
- }
- MessageBox (NULL, debugstring->str, "gspawn-win32-helper", 0);
- }
+ protect_wargv (wargv + argv_zero_offset, &new_wargv);
- if (new_argv[ARG_USE_PATH][0] == 'y')
- handle = spawnvp (mode, __argv[ARG_PROGRAM], new_argv + ARG_PROGRAM + file_and_argv_zero);
+ if (__argv[ARG_USE_PATH][0] == 'y')
+ handle = _wspawnvp (mode, wargv[ARG_PROGRAM], (const wchar_t **) new_wargv);
+ else
+ handle = _wspawnv (mode, wargv[ARG_PROGRAM], (const wchar_t **) new_wargv);
+ }
else
- handle = spawnv (mode, __argv[ARG_PROGRAM], new_argv + ARG_PROGRAM + file_and_argv_zero);
-
- saved_errno = errno;
-
- if (debug)
{
- debugstring = g_string_new (NULL);
- g_string_append (debugstring,
- g_strdup_printf ("%s returned %#x",
- (__argv[ARG_USE_PATH][0] == 'y' ?
- "spawnvp" : "spawnv"),
- handle));
- MessageBox (NULL, debugstring->str, "gspawn-win32-helper", 0);
+ protect_argv (__argv + argv_zero_offset, &new_argv);
+
+ if (__argv[ARG_USE_PATH][0] == 'y')
+ handle = spawnvp (mode, __argv[ARG_PROGRAM], (const char **) new_argv);
+ else
+ handle = spawnv (mode, __argv[ARG_PROGRAM], (const char **) new_argv);
}
+ saved_errno = errno;
+
if (handle == -1 && saved_errno != 0)
write_err_and_exit (child_err_report_fd, CHILD_SPAWN_FAILED);
write (child_err_report_fd, &zero, sizeof (zero));
return 0;
}
-
#include <process.h>
#include <direct.h>
+#ifdef __MINGW32__
+/* Mingw doesn't have prototypes for these */
+int _wspawnvpe (int, const wchar_t *, const wchar_t **, const wchar_t **);
+int _wspawnvp (int, const wchar_t *, const wchar_t **);
+int _wspawnve (int, const wchar_t *, const wchar_t **, const wchar_t **);
+int _wspawnv (int, const wchar_t *, const wchar_t **);
+#endif
+
#include "glibintl.h"
#ifdef G_SPAWN_WIN32_DEBUG
if (need_dblquotes)
*q++ = '"';
*q++ = '\0';
- /* printf ("argv[%d]:%s, need_dblquotes:%s len:%d => %s\n", i, argv[i], need_dblquotes?"TRUE":"FALSE", len, (*new_argv)[i]); */
}
(*new_argv)[argc] = NULL;
}
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)
+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_handle,
- 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()
}
}
+static wchar_t **
+utf8_charv_to_wcharv (gchar **utf8_charv)
+{
+ wchar_t **retval = 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, NULL);
+ retval[n] = NULL;
+ }
+ return retval;
+}
+
+static char **
+utf8_charv_to_cp_charv (gchar **utf8_charv)
+{
+ char **retval = NULL;
+
+ if (utf8_charv != NULL)
+ {
+ int n = 0, i;
+
+ while (utf8_charv[n])
+ n++;
+ retval = g_new (char *, n + 1);
+
+ for (i = 0; i < n; i++)
+ retval[i] = g_locale_from_utf8 (utf8_charv[i], -1, NULL, NULL, NULL);
+ retval[n] = NULL;
+ }
+ return retval;
+}
+
static gboolean
do_spawn_with_pipes (gboolean dont_wait,
gboolean dont_return_handle,
* Unix probably isn't of much use as such on Win32, anyhow.
*/
if (child_setup)
- (* child_setup) (user_data);
+ {
+ static gboolean warned = FALSE;
+ if (!warned)
+ {
+ warned = TRUE;
+ g_warning ("passing a child setup function to the g_spawn functions is pointless and dangerous on Win32");
+ }
+ (* child_setup) (user_data);
+ }
new_argv = (flags & G_SPAWN_FILE_AND_ARGV_ZERO) ? protected_argv + 1 : protected_argv;
- if (flags & G_SPAWN_SEARCH_PATH)
- if (envp != NULL)
- rc = spawnvpe (mode, argv[0], (const char **) new_argv, (const char **) envp);
- else
- rc = spawnvp (mode, argv[0], (const char **) new_argv);
+ if (G_WIN32_HAVE_WIDECHAR_API ())
+ {
+ wchar_t *wargv0 = g_utf8_to_utf16 (argv[0], -1, NULL, NULL, NULL);
+ wchar_t **wargv = utf8_charv_to_wcharv (new_argv);
+ wchar_t **wenvp = utf8_charv_to_wcharv (envp);
+
+ 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);
+ }
else
- if (envp != NULL)
- rc = spawnve (mode, argv[0], (const char **) new_argv, (const char **) envp);
- else
- rc = spawnv (mode, argv[0], (const char **) new_argv);
+ {
+ char *cpargv0 = g_locale_from_utf8 (argv[0], -1, NULL, NULL, NULL);
+ char **cpargv = utf8_charv_to_cp_charv (new_argv);
+ char **cpenvp = utf8_charv_to_cp_charv (envp);
+
+ if (flags & G_SPAWN_SEARCH_PATH)
+ if (cpenvp != NULL)
+ rc = spawnvpe (mode, cpargv0, (const char **) cpargv, (const char **) cpenvp);
+ else
+ rc = spawnvp (mode, cpargv0, (const char **) cpargv);
+ else
+ if (envp != NULL)
+ rc = spawnve (mode, cpargv0, (const char **) cpargv, (const char **) cpenvp);
+ else
+ rc = spawnv (mode, cpargv0, (const char **) cpargv);
+
+ g_free (cpargv0);
+ g_strfreev (cpargv);
+ g_strfreev (cpenvp);
+ }
saved_errno = errno;
- for (i = 0; i < argc; i++)
- g_free (protected_argv[i]);
- g_free (protected_argv);
+ g_strfreev (protected_argv);
if (rc == -1 && saved_errno != 0)
{
if (child_setup)
(* child_setup) (user_data);
- if (envp != NULL)
- /* Let's hope envp hasn't mucked with PATH so that
- * gspawn-win32-helper.exe isn't found.
- */
- rc = spawnvpe (P_NOWAIT, HELPER_PROCESS, (const char **) new_argv, (const char **) envp);
+ if (G_WIN32_HAVE_WIDECHAR_API ())
+ {
+ wchar_t *whelper = g_utf8_to_utf16 (HELPER_PROCESS, -1, NULL, NULL, NULL);
+ wchar_t **wargv = utf8_charv_to_wcharv (new_argv);
+ wchar_t **wenvp = utf8_charv_to_wcharv (envp);
+
+ if (wenvp != NULL)
+ /* Let's hope envp hasn't mucked with PATH so that
+ * gspawn-win32-helper.exe isn't found.
+ */
+ 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);
+ }
else
- rc = spawnvp (P_NOWAIT, HELPER_PROCESS, (const char **) new_argv);
+ {
+ char **cpargv = utf8_charv_to_cp_charv (new_argv);
+ char **cpenvp = utf8_charv_to_cp_charv (envp);
+
+ if (cpenvp != NULL)
+ rc = spawnvpe (P_NOWAIT, HELPER_PROCESS, (const char **) cpargv, (const char **) cpenvp);
+ else
+ rc = spawnvp (P_NOWAIT, HELPER_PROCESS, (const char **) cpargv);
- saved_errno = errno;
+ saved_errno = errno;
+
+ g_strfreev (cpargv);
+ g_strfreev (cpenvp);
+ }
- /* Close thed the other process's ends of the pipes in this
- * process, otherwise the reader will never get EOF.
+ /* 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 (&stdin_pipe[0]);
close_and_invalidate (&stdout_pipe[1]);
close_and_invalidate (&stderr_pipe[1]);
- for (i = 0; i < argc; i++)
- g_free (protected_argv[i]);
- g_free (protected_argv);
+ g_strfreev (protected_argv);
g_free (new_argv[ARG_WORKING_DIRECTORY]);
g_free (new_argv);
}
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)
+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 outpipe = -1;
gint errpipe = -1;
}
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)
+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 ||
}
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);
+}
+
+/* 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
+ {
+ *utf8_working_directory = g_locale_to_utf8 (working_directory, -1, NULL, NULL, NULL);
+ if (*utf8_working_directory == NULL)
+ g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+ "Invalid characters in working directory");
+ return FALSE;
+ }
+
+ argc = 0;
+ while (argv[argc])
+ ++argc;
+ *utf8_argv = g_new (gchar *, argc + 1);
+ for (i = 0; i < argc; i++)
+ {
+ (*utf8_argv)[i] = g_locale_to_utf8 (argv[i], -1, NULL, NULL, NULL);
+ if ((*utf8_argv)[i] == NULL)
+ {
+ g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+ "Invalid characters in argument vector");
+
+ 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
+ {
+ envc = 0;
+ while (envp[envc])
+ ++envc;
+ *utf8_envp = g_new (gchar *, envc + 1);
+ for (i = 0; i < envc; i++)
+ {
+ (*utf8_envp)[i] = g_locale_to_utf8 (envp[i], -1, NULL, NULL, NULL);
+ if ((*utf8_envp)[i] == NULL)
+ {
+ g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+ "Invalid characters in environment");
+
+ 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;
+}
+
+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);
+}
+
+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;
+
+ if (!setup_utf8_copies (working_directory, &utf8_working_directory,
+ argv, &utf8_argv,
+ envp, &utf8_envp,
+ error))
+ return FALSE;
+
+ 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);
+
+ free_utf8_copies (utf8_working_directory, utf8_argv, utf8_envp);
+
+ return retval;
+}
+
+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);
+}
+
+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 retval;
+}
+
+gboolean
g_spawn_command_line_sync (const gchar *command_line,
- gchar **standard_output,
- gchar **standard_error,
- gint *exit_status,
- GError **error)
+ gchar **standard_output,
+ gchar **standard_error,
+ gint *exit_status,
+ GError **error)
{
gboolean retval;
gchar **argv = 0;
gboolean
g_spawn_command_line_async (const gchar *command_line,
- GError **error)
+ GError **error)
{
gboolean retval;
gchar **argv = 0;
#endif /* !GSPAWN_HELPER */
-void
-g_spawn_close_pid (GPid pid)
-{
- CloseHandle (pid);
-}
-
#define __G_SPAWN_C__
#include "galiasdef.c"
* 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.
+ * This function calls g_spawn_async_with_pipes() internally; see that
+ * function for full details on the other parameters and details on
+ * how these functions work on Windows.
*
* Return value: %TRUE on success, %FALSE if an error was set.
**/
/**
* 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
+ * @working_directory: child's current working directory, or %NULL to inherit parent's, in the GLib file name encoding
+ * @argv: child's argument vector, in the GLib file name encoding
+ * @envp: child's environment, or %NULL to inherit parent's, in the GLib file name encoding
* @flags: flags from #GSpawnFlags
* @child_setup: function to run in the child just before exec()
* @user_data: user data for @child_setup
* the program must be a full path; the <envar>PATH</envar> shell variable
* will only be searched if you pass the %G_SPAWN_SEARCH_PATH flag.
*
- * On Windows, the low-level child process creation API
- * (CreateProcess())doesn't use argument vectors,
+ * On Windows, note that all the string or string vector arguments to
+ * this function and the other g_spawn*() functions are in UTF-8, the
+ * GLib file name encoding. Unicode characters that are not part of
+ * the system codepage passed in argument vectors will be correctly
+ * available in the spawned program only if it uses wide character API
+ * to retrieve its command line. For C programs built with Microsoft's
+ * tools it is enough to make the program have a wmain() instead of
+ * main(). wmain() has a wide character argument vector as parameter.
+ *
+ * At least currently, mingw doesn't support wmain(), so if you use
+ * mingw to develop the spawned program, it will have to call the
+ * undocumented function __wgetmainargs() to get the wide character
+ * argument vector and environment. See gspawn-win32-helper.c in the
+ * GLib sources or init.c in the mingw runtime sources for a prototype
+ * for that function. Alternatively, you can retrieve the Win32 system
+ * level wide character command line passed to the spawned program
+ * using the GetCommandLineW() function.
+ *
+ * On Windows the low-level child process creation API
+ * <function>CreateProcess()</function> doesn't use argument vectors,
* but a command line. The C runtime library's
* <function>spawn*()</function> family of functions (which
* g_spawn_async_with_pipes() eventually calls) paste the argument
- * vector elements into a command line, and the C runtime startup code
+ * vector elements together into a command line, and the C runtime startup code
* does a corresponding reconstruction of an argument vector from the
* command line, to be passed to main(). Complications arise when you have
* argument vector elements that contain spaces of double quotes. The
* before calling exec() in the child. Obviously
* actions taken in this function will only affect the child, not the
* parent. On Windows, there is no separate fork() and exec()
- * functionality. Child processes are created and run right away with
- * one API call, CreateProcess(). @child_setup is
+ * functionality. Child processes are created and run with
+ * a single API call, CreateProcess(). @child_setup is
* called in the parent process just before creating the child
* process. You should carefully consider what you do in @child_setup
* if you intend your software to be portable to Windows.
* process using the Win32 API, for example wait for its termination
* with the <function>WaitFor*()</function> functions, or examine its
* exit code with GetExitCodeProcess(). You should close the handle
- * with CloseHandle() when you no longer need it.
+ * with CloseHandle() or g_spawn_close_pid() when you no longer need it.
*
* If non-%NULL, the @standard_input, @standard_output, @standard_error
* locations will be filled with file descriptors for writing to the child's
* and WEXITSTATUS() must be used to evaluate the exit status.
*
* On Windows, please note the implications of g_shell_parse_argv()
- * parsing @command_line. Space is a separator, and backslashes are
+ * parsing @command_line. Parsing is done according to Unix shell rules, not
+ * Windows command interpreter rules.
+ * Space is a separator, and backslashes are
* special. Thus you cannot simply pass a @command_line containing
* canonical Windows paths, like "c:\\program files\\app\\app.exe", as
* the backslashes will be eaten, and the space will act as a
GQuark g_spawn_error_quark (void);
+#ifdef G_OS_WIN32
+#define g_spawn_async g_spawn_async_utf8
+#define g_spawn_async_with_pipes g_spawn_async_with_pipes_utf8
+#define g_spawn_sync g_spawn_sync_utf8
+#define g_spawn_command_line_sync g_spawn_command_line_sync_utf8
+#define g_spawn_command_line_async g_spawn_command_line_async_utf8
+#endif
+
gboolean g_spawn_async (const gchar *working_directory,
gchar **argv,
gchar **envp,