Make also the g_spawn*() functions take parameters in the GLib file name

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.
This commit is contained in:
Tor Lillqvist 2005-08-25 23:28:24 +00:00 committed by Tor Lillqvist
parent 2982892d98
commit 41e833ae4c
8 changed files with 730 additions and 175 deletions

View File

@ -1,3 +1,35 @@
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> 2005-08-24 Stepan Kasal <kasal@ucw.cz>
* glib/gtypes.h (G_MININT64): Cast the constant to gint64; it is * glib/gtypes.h (G_MININT64): Cast the constant to gint64; it is

View File

@ -1,3 +1,35 @@
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> 2005-08-24 Stepan Kasal <kasal@ucw.cz>
* glib/gtypes.h (G_MININT64): Cast the constant to gint64; it is * glib/gtypes.h (G_MININT64): Cast the constant to gint64; it is

View File

@ -1,3 +1,35 @@
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> 2005-08-24 Stepan Kasal <kasal@ucw.cz>
* glib/gtypes.h (G_MININT64): Cast the constant to gint64; it is * glib/gtypes.h (G_MININT64): Cast the constant to gint64; it is

View File

@ -851,13 +851,20 @@ g_slist_sort_with_data
#if IN_HEADER(__G_SPAWN_H__) #if IN_HEADER(__G_SPAWN_H__)
#if IN_FILE(__G_SPAWN_C__) #if IN_FILE(__G_SPAWN_C__)
g_spawn_async g_spawn_async PRIVATE
g_spawn_async_with_pipes g_spawn_async_with_pipes PRIVATE
g_spawn_close_pid g_spawn_close_pid
g_spawn_command_line_async g_spawn_command_line_async PRIVATE
g_spawn_command_line_sync g_spawn_command_line_sync PRIVATE
g_spawn_error_quark 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
#endif #endif

View File

@ -27,23 +27,12 @@
#include "gspawn-win32.c" /* For shared definitions */ #include "gspawn-win32.c" /* For shared definitions */
static GString *debugstring;
static void static void
write_err_and_exit (gint fd, write_err_and_exit (gint fd,
gint msg) gint msg)
{ {
gint en = errno; 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, &msg, sizeof(msg));
write (fd, &en, sizeof(en)); write (fd, &en, sizeof(en));
@ -63,6 +52,99 @@ write_err_and_exit (gint fd,
* away in the global __argc and __argv by the C runtime startup code. * 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 int _stdcall
WinMain (struct HINSTANCE__ *hInstance, WinMain (struct HINSTANCE__ *hInstance,
struct HINSTANCE__ *hPrevInstance, struct HINSTANCE__ *hPrevInstance,
@ -77,44 +159,43 @@ WinMain (struct HINSTANCE__ *hInstance,
int saved_errno; int saved_errno;
int no_error = CHILD_NO_ERROR; int no_error = CHILD_NO_ERROR;
int zero = 0; int zero = 0;
gint file_and_argv_zero = 0; gint argv_zero_offset = ARG_PROGRAM;
gchar **new_argv; gchar **new_argv;
wchar_t **new_wargv;
SETUP_DEBUG(); int argc;
wchar_t **wargv, **wenvp;
if (debug) _startupinfo si = { 0 };
{
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);
}
g_assert (__argc >= ARG_COUNT); g_assert (__argc >= ARG_COUNT);
/* argv[ARG_CHILD_ERR_REPORT] is the file descriptor onto which if (G_WIN32_HAVE_WIDECHAR_API ())
* write error messages. {
/* 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);
}
/* 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]); 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] == '#') 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 /* argv[ARG_STDIN..ARG_STDERR] are the file descriptor numbers that
* be dup2'd to stdin, stdout and stderr, '-' if the corresponding * should be dup2'd to 0, 1 and 2. '-' if the corresponding fd
* std* should be let alone, and 'z' if it should be connected to * should be left alone, and 'z' if it should be connected to the
* the bit bucket NUL:. * bit bucket NUL:.
*/ */
if (__argv[ARG_STDIN][0] == '-') if (__argv[ARG_STDIN][0] == '-')
; /* Nothing */ ; /* Nothing */
@ -185,21 +266,21 @@ WinMain (struct HINSTANCE__ *hInstance,
if (__argv[ARG_WORKING_DIRECTORY][0] == '-' && if (__argv[ARG_WORKING_DIRECTORY][0] == '-' &&
__argv[ARG_WORKING_DIRECTORY][1] == 0) __argv[ARG_WORKING_DIRECTORY][1] == 0)
; /* Nothing */ ; /* Nothing */
else if (chdir (__argv[ARG_WORKING_DIRECTORY]) < 0) else if ((G_WIN32_HAVE_WIDECHAR_API () &&
write_err_and_exit (child_err_report_fd, _wchdir (wargv[ARG_WORKING_DIRECTORY]) < 0) ||
CHILD_CHDIR_FAILED); (!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 /* __argv[ARG_CLOSE_DESCRIPTORS] is "y" if file descriptors from 3
* upwards should be closed * upwards should be closed
*/ */
if (__argv[ARG_CLOSE_DESCRIPTORS][0] == 'y') if (__argv[ARG_CLOSE_DESCRIPTORS][0] == 'y')
for (i = 3; i < 1000; i++) /* FIXME real limit? */ for (i = 3; i < 1000; i++) /* FIXME real limit? */
if (i != child_err_report_fd) if (i != child_err_report_fd)
close (i); close (i);
/* __argv[ARG_WAIT] is "w" to wait for the program to exit */ /* __argv[ARG_WAIT] is "w" to wait for the program to exit */
if (__argv[ARG_WAIT][0] == 'w') if (__argv[ARG_WAIT][0] == 'w')
mode = P_WAIT; mode = P_WAIT;
else else
@ -207,53 +288,36 @@ WinMain (struct HINSTANCE__ *hInstance,
/* __argv[ARG_USE_PATH] is "y" to use PATH, otherwise not */ /* __argv[ARG_USE_PATH] is "y" to use PATH, otherwise not */
/* __argv[ARG_PROGRAM] is program file to run, /* __argv[ARG_PROGRAM] is executable file to run,
* __argv[ARG_PROGRAM+1]... is its __argv. * __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 /* For the program name passed to spawnv(), don't use the quoted
* version. */ * version.
*/
if (debug) if (G_WIN32_HAVE_WIDECHAR_API ())
{ {
debugstring = g_string_new (NULL); protect_wargv (wargv + argv_zero_offset, &new_wargv);
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);
}
if (new_argv[ARG_USE_PATH][0] == 'y') if (__argv[ARG_USE_PATH][0] == 'y')
handle = spawnvp (mode, __argv[ARG_PROGRAM], new_argv + ARG_PROGRAM + file_and_argv_zero); handle = _wspawnvp (mode, wargv[ARG_PROGRAM], (const wchar_t **) new_wargv);
else
handle = _wspawnv (mode, wargv[ARG_PROGRAM], (const wchar_t **) new_wargv);
}
else else
handle = spawnv (mode, __argv[ARG_PROGRAM], new_argv + ARG_PROGRAM + file_and_argv_zero); {
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; 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);
}
if (handle == -1 && saved_errno != 0) if (handle == -1 && saved_errno != 0)
write_err_and_exit (child_err_report_fd, CHILD_SPAWN_FAILED); write_err_and_exit (child_err_report_fd, CHILD_SPAWN_FAILED);
@ -264,4 +328,3 @@ WinMain (struct HINSTANCE__ *hInstance,
write (child_err_report_fd, &zero, sizeof (zero)); write (child_err_report_fd, &zero, sizeof (zero));
return 0; return 0;
} }

View File

@ -59,6 +59,14 @@
#include <process.h> #include <process.h>
#include <direct.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" #include "glibintl.h"
#ifdef G_SPAWN_WIN32_DEBUG #ifdef G_SPAWN_WIN32_DEBUG
@ -171,7 +179,6 @@ protect_argv (gchar **argv,
if (need_dblquotes) if (need_dblquotes)
*q++ = '"'; *q++ = '"';
*q++ = '\0'; *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; (*new_argv)[argc] = NULL;
@ -192,25 +199,25 @@ g_spawn_error_quark (void)
} }
gboolean gboolean
g_spawn_async (const gchar *working_directory, g_spawn_async_utf8 (const gchar *working_directory,
gchar **argv, gchar **argv,
gchar **envp, gchar **envp,
GSpawnFlags flags, GSpawnFlags flags,
GSpawnChildSetupFunc child_setup, GSpawnChildSetupFunc child_setup,
gpointer user_data, gpointer user_data,
GPid *child_handle, GPid *child_handle,
GError **error) GError **error)
{ {
g_return_val_if_fail (argv != NULL, FALSE); g_return_val_if_fail (argv != NULL, FALSE);
return g_spawn_async_with_pipes (working_directory, return g_spawn_async_with_pipes_utf8 (working_directory,
argv, envp, argv, envp,
flags, flags,
child_setup, child_setup,
user_data, user_data,
child_handle, child_handle,
NULL, NULL, NULL, NULL, NULL, NULL,
error); error);
} }
/* Avoids a danger in threaded situations (calling close() /* Avoids a danger in threaded situations (calling close()
@ -362,6 +369,46 @@ set_child_error (gint report[2],
} }
} }
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 static gboolean
do_spawn_with_pipes (gboolean dont_wait, do_spawn_with_pipes (gboolean dont_wait,
gboolean dont_return_handle, gboolean dont_return_handle,
@ -414,25 +461,63 @@ do_spawn_with_pipes (gboolean dont_wait,
* Unix probably isn't of much use as such on Win32, anyhow. * Unix probably isn't of much use as such on Win32, anyhow.
*/ */
if (child_setup) 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; new_argv = (flags & G_SPAWN_FILE_AND_ARGV_ZERO) ? protected_argv + 1 : protected_argv;
if (flags & G_SPAWN_SEARCH_PATH) if (G_WIN32_HAVE_WIDECHAR_API ())
if (envp != NULL) {
rc = spawnvpe (mode, argv[0], (const char **) new_argv, (const char **) envp); wchar_t *wargv0 = g_utf8_to_utf16 (argv[0], -1, NULL, NULL, NULL);
else wchar_t **wargv = utf8_charv_to_wcharv (new_argv);
rc = spawnvp (mode, argv[0], (const char **) 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 else
if (envp != NULL) {
rc = spawnve (mode, argv[0], (const char **) new_argv, (const char **) envp); char *cpargv0 = g_locale_from_utf8 (argv[0], -1, NULL, NULL, NULL);
else char **cpargv = utf8_charv_to_cp_charv (new_argv);
rc = spawnv (mode, argv[0], (const char **) 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; saved_errno = errno;
for (i = 0; i < argc; i++) g_strfreev (protected_argv);
g_free (protected_argv[i]);
g_free (protected_argv);
if (rc == -1 && saved_errno != 0) if (rc == -1 && saved_errno != 0)
{ {
@ -565,27 +650,51 @@ do_spawn_with_pipes (gboolean dont_wait,
if (child_setup) if (child_setup)
(* child_setup) (user_data); (* child_setup) (user_data);
if (envp != NULL) if (G_WIN32_HAVE_WIDECHAR_API ())
/* Let's hope envp hasn't mucked with PATH so that {
* gspawn-win32-helper.exe isn't found. wchar_t *whelper = g_utf8_to_utf16 (HELPER_PROCESS, -1, NULL, NULL, NULL);
*/ wchar_t **wargv = utf8_charv_to_wcharv (new_argv);
rc = spawnvpe (P_NOWAIT, HELPER_PROCESS, (const char **) new_argv, (const char **) envp); 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 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);
saved_errno = errno; 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);
/* Close thed the other process's ends of the pipes in this saved_errno = errno;
* process, otherwise the reader will never get EOF.
g_strfreev (cpargv);
g_strfreev (cpenvp);
}
/* 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 (&child_err_report_pipe[1]);
close_and_invalidate (&stdin_pipe[0]); close_and_invalidate (&stdin_pipe[0]);
close_and_invalidate (&stdout_pipe[1]); close_and_invalidate (&stdout_pipe[1]);
close_and_invalidate (&stderr_pipe[1]); close_and_invalidate (&stderr_pipe[1]);
for (i = 0; i < argc; i++) g_strfreev (protected_argv);
g_free (protected_argv[i]);
g_free (protected_argv);
g_free (new_argv[ARG_WORKING_DIRECTORY]); g_free (new_argv[ARG_WORKING_DIRECTORY]);
g_free (new_argv); g_free (new_argv);
@ -680,16 +789,16 @@ do_spawn_with_pipes (gboolean dont_wait,
} }
gboolean gboolean
g_spawn_sync (const gchar *working_directory, g_spawn_sync_utf8 (const gchar *working_directory,
gchar **argv, gchar **argv,
gchar **envp, gchar **envp,
GSpawnFlags flags, GSpawnFlags flags,
GSpawnChildSetupFunc child_setup, GSpawnChildSetupFunc child_setup,
gpointer user_data, gpointer user_data,
gchar **standard_output, gchar **standard_output,
gchar **standard_error, gchar **standard_error,
gint *exit_status, gint *exit_status,
GError **error) GError **error)
{ {
gint outpipe = -1; gint outpipe = -1;
gint errpipe = -1; gint errpipe = -1;
@ -920,17 +1029,17 @@ g_spawn_sync (const gchar *working_directory,
} }
gboolean gboolean
g_spawn_async_with_pipes (const gchar *working_directory, g_spawn_async_with_pipes_utf8 (const gchar *working_directory,
gchar **argv, gchar **argv,
gchar **envp, gchar **envp,
GSpawnFlags flags, GSpawnFlags flags,
GSpawnChildSetupFunc child_setup, GSpawnChildSetupFunc child_setup,
gpointer user_data, gpointer user_data,
GPid *child_handle, GPid *child_handle,
gint *standard_input, gint *standard_input,
gint *standard_output, gint *standard_output,
gint *standard_error, gint *standard_error,
GError **error) GError **error)
{ {
g_return_val_if_fail (argv != NULL, FALSE); g_return_val_if_fail (argv != NULL, FALSE);
g_return_val_if_fail (standard_output == NULL || g_return_val_if_fail (standard_output == NULL ||
@ -958,12 +1067,269 @@ g_spawn_async_with_pipes (const gchar *working_directory,
error); 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);
}
/* 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 gboolean
g_spawn_command_line_sync (const gchar *command_line, g_spawn_command_line_sync (const gchar *command_line,
gchar **standard_output, gchar **standard_output,
gchar **standard_error, gchar **standard_error,
gint *exit_status, gint *exit_status,
GError **error) GError **error)
{ {
gboolean retval; gboolean retval;
gchar **argv = 0; gchar **argv = 0;
@ -992,7 +1358,7 @@ g_spawn_command_line_sync (const gchar *command_line,
gboolean gboolean
g_spawn_command_line_async (const gchar *command_line, g_spawn_command_line_async (const gchar *command_line,
GError **error) GError **error)
{ {
gboolean retval; gboolean retval;
gchar **argv = 0; gchar **argv = 0;
@ -1019,11 +1385,5 @@ g_spawn_command_line_async (const gchar *command_line,
#endif /* !GSPAWN_HELPER */ #endif /* !GSPAWN_HELPER */
void
g_spawn_close_pid (GPid pid)
{
CloseHandle (pid);
}
#define __G_SPAWN_C__ #define __G_SPAWN_C__
#include "galiasdef.c" #include "galiasdef.c"

View File

@ -206,8 +206,9 @@ read_data (GString *str,
* must be used to evaluate the exit status. If an error occurs, no data is * must be used to evaluate the exit status. If an error occurs, no data is
* returned in @standard_output, @standard_error, or @exit_status. * returned in @standard_output, @standard_error, or @exit_status.
* *
* This function calls g_spawn_async_with_pipes() internally; see that function * This function calls g_spawn_async_with_pipes() internally; see that
* for full details on the other parameters. * 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. * Return value: %TRUE on success, %FALSE if an error was set.
**/ **/
@ -422,9 +423,9 @@ g_spawn_sync (const gchar *working_directory,
/** /**
* g_spawn_async_with_pipes: * g_spawn_async_with_pipes:
* @working_directory: child's current working directory, 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 * @argv: child's argument vector, in the GLib file name encoding
* @envp: child's environment, or %NULL to inherit parent's * @envp: child's environment, or %NULL to inherit parent's, in the GLib file name encoding
* @flags: flags from #GSpawnFlags * @flags: flags from #GSpawnFlags
* @child_setup: function to run in the child just before exec() * @child_setup: function to run in the child just before exec()
* @user_data: user data for @child_setup * @user_data: user data for @child_setup
@ -443,12 +444,30 @@ g_spawn_sync (const gchar *working_directory,
* the program must be a full path; the <envar>PATH</envar> shell variable * 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. * will only be searched if you pass the %G_SPAWN_SEARCH_PATH flag.
* *
* On Windows, the low-level child process creation API * On Windows, note that all the string or string vector arguments to
* (CreateProcess())doesn't use argument vectors, * 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 * but a command line. The C runtime library's
* <function>spawn*()</function> family of functions (which * <function>spawn*()</function> family of functions (which
* g_spawn_async_with_pipes() eventually calls) paste the argument * 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 * does a corresponding reconstruction of an argument vector from the
* command line, to be passed to main(). Complications arise when you have * command line, to be passed to main(). Complications arise when you have
* argument vector elements that contain spaces of double quotes. The * argument vector elements that contain spaces of double quotes. The
@ -506,8 +525,8 @@ g_spawn_sync (const gchar *working_directory,
* before calling exec() in the child. Obviously * before calling exec() in the child. Obviously
* actions taken in this function will only affect the child, not the * actions taken in this function will only affect the child, not the
* parent. On Windows, there is no separate fork() and exec() * parent. On Windows, there is no separate fork() and exec()
* functionality. Child processes are created and run right away with * functionality. Child processes are created and run with
* one API call, CreateProcess(). @child_setup is * a single API call, CreateProcess(). @child_setup is
* called in the parent process just before creating the child * called in the parent process just before creating the child
* process. You should carefully consider what you do in @child_setup * process. You should carefully consider what you do in @child_setup
* if you intend your software to be portable to Windows. * if you intend your software to be portable to Windows.
@ -521,7 +540,7 @@ g_spawn_sync (const gchar *working_directory,
* process using the Win32 API, for example wait for its termination * process using the Win32 API, for example wait for its termination
* with the <function>WaitFor*()</function> functions, or examine its * with the <function>WaitFor*()</function> functions, or examine its
* exit code with GetExitCodeProcess(). You should close the handle * 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 * If non-%NULL, the @standard_input, @standard_output, @standard_error
* locations will be filled with file descriptors for writing to the child's * locations will be filled with file descriptors for writing to the child's
@ -617,7 +636,9 @@ g_spawn_async_with_pipes (const gchar *working_directory,
* and WEXITSTATUS() must be used to evaluate the exit status. * and WEXITSTATUS() must be used to evaluate the exit status.
* *
* On Windows, please note the implications of g_shell_parse_argv() * 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 * special. Thus you cannot simply pass a @command_line containing
* canonical Windows paths, like "c:\\program files\\app\\app.exe", as * canonical Windows paths, like "c:\\program files\\app\\app.exe", as
* the backslashes will be eaten, and the space will act as a * the backslashes will be eaten, and the space will act as a

View File

@ -71,6 +71,14 @@ typedef enum
GQuark g_spawn_error_quark (void); 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, gboolean g_spawn_async (const gchar *working_directory,
gchar **argv, gchar **argv,
gchar **envp, gchar **envp,