Make also the g_spawn*() functions take parameters in the GLib file name
authorTor Lillqvist <tml@novell.com>
Thu, 25 Aug 2005 23:28:24 +0000 (23:28 +0000)
committerTor Lillqvist <tml@src.gnome.org>
Thu, 25 Aug 2005 23:28:24 +0000 (23:28 +0000)
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.

ChangeLog
ChangeLog.pre-2-10
ChangeLog.pre-2-12
glib/glib.symbols
glib/gspawn-win32-helper.c
glib/gspawn-win32.c
glib/gspawn.c
glib/gspawn.h

index 3836d44..b0d9efd 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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>
 
        * glib/gtypes.h (G_MININT64): Cast the constant to gint64; it is
index 3836d44..b0d9efd 100644 (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>
 
        * glib/gtypes.h (G_MININT64): Cast the constant to gint64; it is
index 3836d44..b0d9efd 100644 (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>
 
        * glib/gtypes.h (G_MININT64): Cast the constant to gint64; it is
index 685357e..bb205c6 100644 (file)
@@ -851,13 +851,20 @@ g_slist_sort_with_data
 
 #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
 
index 1e34aab..1f79fa4 100644 (file)
 #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));
   
@@ -63,6 +52,99 @@ write_err_and_exit (gint fd,
  * 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,
@@ -77,44 +159,43 @@ WinMain (struct HINSTANCE__ *hInstance,
   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 */
@@ -185,21 +266,21 @@ WinMain (struct HINSTANCE__ *hInstance,
   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
@@ -207,53 +288,36 @@ WinMain (struct HINSTANCE__ *hInstance,
 
   /* __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);
 
@@ -264,4 +328,3 @@ WinMain (struct HINSTANCE__ *hInstance,
     write (child_err_report_fd, &zero, sizeof (zero));
   return 0;
 }
-
index 403a49d..423aa54 100644 (file)
 #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
@@ -171,7 +179,6 @@ protect_argv (gchar  **argv,
       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;
 
@@ -192,25 +199,25 @@ g_spawn_error_quark (void)
 }
 
 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()
@@ -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
 do_spawn_with_pipes (gboolean              dont_wait,
                     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.
        */
       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)
        {
@@ -565,27 +650,51 @@ do_spawn_with_pipes (gboolean              dont_wait,
   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);
@@ -680,16 +789,16 @@ do_spawn_with_pipes (gboolean              dont_wait,
 }
 
 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;
@@ -920,17 +1029,17 @@ g_spawn_sync (const gchar          *working_directory,
 }
 
 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 ||
@@ -959,11 +1068,268 @@ g_spawn_async_with_pipes (const gchar          *working_directory,
 }
 
 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;
@@ -992,7 +1358,7 @@ g_spawn_command_line_sync (const gchar  *command_line,
 
 gboolean
 g_spawn_command_line_async (const gchar *command_line,
-                            GError     **error)
+                           GError     **error)
 {
   gboolean retval;
   gchar **argv = 0;
@@ -1019,11 +1385,5 @@ g_spawn_command_line_async (const gchar *command_line,
 
 #endif /* !GSPAWN_HELPER */
 
-void
-g_spawn_close_pid (GPid pid)
-{
-    CloseHandle (pid);
-}
-
 #define __G_SPAWN_C__
 #include "galiasdef.c"
index 48f321c..4c7fa91 100644 (file)
@@ -206,8 +206,9 @@ read_data (GString *str,
  * 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.
  **/
@@ -422,9 +423,9 @@ g_spawn_sync (const gchar          *working_directory,
 
 /**
  * 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
@@ -443,12 +444,30 @@ g_spawn_sync (const gchar          *working_directory,
  * 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
@@ -506,8 +525,8 @@ g_spawn_sync (const gchar          *working_directory,
  * 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.
@@ -521,7 +540,7 @@ g_spawn_sync (const gchar          *working_directory,
  * 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
@@ -617,7 +636,9 @@ g_spawn_async_with_pipes (const gchar          *working_directory,
  * 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
index 9ca1879..adb6741 100644 (file)
@@ -71,6 +71,14 @@ typedef enum
 
 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,