Some more debugging output. (g_io_channel_win32_poll): Remove unused vars.
authorTor Lillqvist <tml@iki.fi>
Mon, 16 Oct 2000 18:54:29 +0000 (18:54 +0000)
committerTor Lillqvist <tml@src.gnome.org>
Mon, 16 Oct 2000 18:54:29 +0000 (18:54 +0000)
2000-10-16  Tor Lillqvist  <tml@iki.fi>

* giowin32.c (reader_thread): Some more debugging output.
(g_io_channel_win32_poll): Remove unused vars.

* gfileutils.c: Changes for Win32, with no unistd.h and no
S_ISLNK().

* gspawn-win32.c: Implementation of the g_spwan_* functions for
Win32. Due to the general non-Unixness of Win32, much of the
functionality that is relatively clean to implement on Unix, is
hard to do on Win32. We must use a separate helper program to
change directory, close extra file descriptors, redirect the std
ones, as needed, and only then start the child process. No child
process pid can be returned, unfortunately. Or if we used
CreateProcess directly, it probably could. (Now we use the spawnv*
functions from msvcrt.)

* glib.def: Add new entry points.

* glib.def
* giowin32.c: Remove g_io_channel_win32_wait_for_condition(),
g_io_channel_win32_poll() subsumes it.

* gbacktrace.h: G_BREAKPOINT for MSVC (on the ix86).

* gwin32.c (g_win32_getlocale): Use "sp" for
LANG_CROATIAN+SUBLANG_SERBIAN_LATIN.

* makefile.{mingw,msc}.in (glib_OBJECTS): Add new files.
Add gspawn-win32-helper.exe rule.

* tests/makefile.{mingw,msc}.in (TESTS): Add shell-test and
spawn-test.

* tests/spawn-test.c: (run_tests): On Win32, don't try to run
/bin/sh, but ipconfig (no special significance in choosing that,
just a program that outputs something to stdout).

27 files changed:
ChangeLog
ChangeLog.pre-2-0
ChangeLog.pre-2-10
ChangeLog.pre-2-12
ChangeLog.pre-2-2
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
gbacktrace.h
gfileutils.c
giowin32.c
glib.def
glib/gbacktrace.h
glib/gfileutils.c
glib/giowin32.c
glib/glib.def
glib/gspawn-win32.c [new file with mode: 0644]
glib/gwin32.c
glib/makefile.mingw.in
glib/makefile.msc.in
gspawn-win32.c [new file with mode: 0644]
gwin32.c
makefile.mingw.in
makefile.msc.in
tests/makefile.mingw.in
tests/makefile.msc.in
tests/spawn-test.c

index 4b6ce0f..6c18f42 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,42 @@
+2000-10-16  Tor Lillqvist  <tml@iki.fi>
+
+       * giowin32.c (reader_thread): Some more debugging output.
+       (g_io_channel_win32_poll): Remove unused vars.
+
+       * gfileutils.c: Changes for Win32, with no unistd.h and no
+       S_ISLNK().
+
+       * gspawn-win32.c: Implementation of the g_spwan_* functions for
+       Win32. Due to the general non-Unixness of Win32, much of the
+       functionality that is relatively clean to implement on Unix, is
+       hard to do on Win32. We must use a separate helper program to
+       change directory, close extra file descriptors, redirect the std
+       ones, as needed, and only then start the child process. No child
+       process pid can be returned, unfortunately. Or if we used
+       CreateProcess directly, it probably could. (Now we use the spawnv*
+       functions from msvcrt.)
+
+       * glib.def: Add new entry points.
+
+       * glib.def
+       * giowin32.c: Remove g_io_channel_win32_wait_for_condition(),
+       g_io_channel_win32_poll() subsumes it.
+
+       * gbacktrace.h: G_BREAKPOINT for MSVC (on the ix86).
+
+       * gwin32.c (g_win32_getlocale): Use "sp" for
+       LANG_CROATIAN+SUBLANG_SERBIAN_LATIN.
+
+       * makefile.{mingw,msc}.in (glib_OBJECTS): Add new files.
+       Add gspawn-win32-helper.exe rule.
+
+       * tests/makefile.{mingw,msc}.in (TESTS): Add shell-test and
+       spawn-test.
+
+       * tests/spawn-test.c: (run_tests): On Win32, don't try to run
+       /bin/sh, but ipconfig (no special significance in choosing that,
+       just a program that outputs something to stdout).
+
 2000-10-15  Raja R Harinath  <harinath@cs.umn.edu>
 
        Remove need for acconfig.h, and misc. cleanups.
index 4b6ce0f..6c18f42 100644 (file)
@@ -1,3 +1,42 @@
+2000-10-16  Tor Lillqvist  <tml@iki.fi>
+
+       * giowin32.c (reader_thread): Some more debugging output.
+       (g_io_channel_win32_poll): Remove unused vars.
+
+       * gfileutils.c: Changes for Win32, with no unistd.h and no
+       S_ISLNK().
+
+       * gspawn-win32.c: Implementation of the g_spwan_* functions for
+       Win32. Due to the general non-Unixness of Win32, much of the
+       functionality that is relatively clean to implement on Unix, is
+       hard to do on Win32. We must use a separate helper program to
+       change directory, close extra file descriptors, redirect the std
+       ones, as needed, and only then start the child process. No child
+       process pid can be returned, unfortunately. Or if we used
+       CreateProcess directly, it probably could. (Now we use the spawnv*
+       functions from msvcrt.)
+
+       * glib.def: Add new entry points.
+
+       * glib.def
+       * giowin32.c: Remove g_io_channel_win32_wait_for_condition(),
+       g_io_channel_win32_poll() subsumes it.
+
+       * gbacktrace.h: G_BREAKPOINT for MSVC (on the ix86).
+
+       * gwin32.c (g_win32_getlocale): Use "sp" for
+       LANG_CROATIAN+SUBLANG_SERBIAN_LATIN.
+
+       * makefile.{mingw,msc}.in (glib_OBJECTS): Add new files.
+       Add gspawn-win32-helper.exe rule.
+
+       * tests/makefile.{mingw,msc}.in (TESTS): Add shell-test and
+       spawn-test.
+
+       * tests/spawn-test.c: (run_tests): On Win32, don't try to run
+       /bin/sh, but ipconfig (no special significance in choosing that,
+       just a program that outputs something to stdout).
+
 2000-10-15  Raja R Harinath  <harinath@cs.umn.edu>
 
        Remove need for acconfig.h, and misc. cleanups.
index 4b6ce0f..6c18f42 100644 (file)
@@ -1,3 +1,42 @@
+2000-10-16  Tor Lillqvist  <tml@iki.fi>
+
+       * giowin32.c (reader_thread): Some more debugging output.
+       (g_io_channel_win32_poll): Remove unused vars.
+
+       * gfileutils.c: Changes for Win32, with no unistd.h and no
+       S_ISLNK().
+
+       * gspawn-win32.c: Implementation of the g_spwan_* functions for
+       Win32. Due to the general non-Unixness of Win32, much of the
+       functionality that is relatively clean to implement on Unix, is
+       hard to do on Win32. We must use a separate helper program to
+       change directory, close extra file descriptors, redirect the std
+       ones, as needed, and only then start the child process. No child
+       process pid can be returned, unfortunately. Or if we used
+       CreateProcess directly, it probably could. (Now we use the spawnv*
+       functions from msvcrt.)
+
+       * glib.def: Add new entry points.
+
+       * glib.def
+       * giowin32.c: Remove g_io_channel_win32_wait_for_condition(),
+       g_io_channel_win32_poll() subsumes it.
+
+       * gbacktrace.h: G_BREAKPOINT for MSVC (on the ix86).
+
+       * gwin32.c (g_win32_getlocale): Use "sp" for
+       LANG_CROATIAN+SUBLANG_SERBIAN_LATIN.
+
+       * makefile.{mingw,msc}.in (glib_OBJECTS): Add new files.
+       Add gspawn-win32-helper.exe rule.
+
+       * tests/makefile.{mingw,msc}.in (TESTS): Add shell-test and
+       spawn-test.
+
+       * tests/spawn-test.c: (run_tests): On Win32, don't try to run
+       /bin/sh, but ipconfig (no special significance in choosing that,
+       just a program that outputs something to stdout).
+
 2000-10-15  Raja R Harinath  <harinath@cs.umn.edu>
 
        Remove need for acconfig.h, and misc. cleanups.
index 4b6ce0f..6c18f42 100644 (file)
@@ -1,3 +1,42 @@
+2000-10-16  Tor Lillqvist  <tml@iki.fi>
+
+       * giowin32.c (reader_thread): Some more debugging output.
+       (g_io_channel_win32_poll): Remove unused vars.
+
+       * gfileutils.c: Changes for Win32, with no unistd.h and no
+       S_ISLNK().
+
+       * gspawn-win32.c: Implementation of the g_spwan_* functions for
+       Win32. Due to the general non-Unixness of Win32, much of the
+       functionality that is relatively clean to implement on Unix, is
+       hard to do on Win32. We must use a separate helper program to
+       change directory, close extra file descriptors, redirect the std
+       ones, as needed, and only then start the child process. No child
+       process pid can be returned, unfortunately. Or if we used
+       CreateProcess directly, it probably could. (Now we use the spawnv*
+       functions from msvcrt.)
+
+       * glib.def: Add new entry points.
+
+       * glib.def
+       * giowin32.c: Remove g_io_channel_win32_wait_for_condition(),
+       g_io_channel_win32_poll() subsumes it.
+
+       * gbacktrace.h: G_BREAKPOINT for MSVC (on the ix86).
+
+       * gwin32.c (g_win32_getlocale): Use "sp" for
+       LANG_CROATIAN+SUBLANG_SERBIAN_LATIN.
+
+       * makefile.{mingw,msc}.in (glib_OBJECTS): Add new files.
+       Add gspawn-win32-helper.exe rule.
+
+       * tests/makefile.{mingw,msc}.in (TESTS): Add shell-test and
+       spawn-test.
+
+       * tests/spawn-test.c: (run_tests): On Win32, don't try to run
+       /bin/sh, but ipconfig (no special significance in choosing that,
+       just a program that outputs something to stdout).
+
 2000-10-15  Raja R Harinath  <harinath@cs.umn.edu>
 
        Remove need for acconfig.h, and misc. cleanups.
index 4b6ce0f..6c18f42 100644 (file)
@@ -1,3 +1,42 @@
+2000-10-16  Tor Lillqvist  <tml@iki.fi>
+
+       * giowin32.c (reader_thread): Some more debugging output.
+       (g_io_channel_win32_poll): Remove unused vars.
+
+       * gfileutils.c: Changes for Win32, with no unistd.h and no
+       S_ISLNK().
+
+       * gspawn-win32.c: Implementation of the g_spwan_* functions for
+       Win32. Due to the general non-Unixness of Win32, much of the
+       functionality that is relatively clean to implement on Unix, is
+       hard to do on Win32. We must use a separate helper program to
+       change directory, close extra file descriptors, redirect the std
+       ones, as needed, and only then start the child process. No child
+       process pid can be returned, unfortunately. Or if we used
+       CreateProcess directly, it probably could. (Now we use the spawnv*
+       functions from msvcrt.)
+
+       * glib.def: Add new entry points.
+
+       * glib.def
+       * giowin32.c: Remove g_io_channel_win32_wait_for_condition(),
+       g_io_channel_win32_poll() subsumes it.
+
+       * gbacktrace.h: G_BREAKPOINT for MSVC (on the ix86).
+
+       * gwin32.c (g_win32_getlocale): Use "sp" for
+       LANG_CROATIAN+SUBLANG_SERBIAN_LATIN.
+
+       * makefile.{mingw,msc}.in (glib_OBJECTS): Add new files.
+       Add gspawn-win32-helper.exe rule.
+
+       * tests/makefile.{mingw,msc}.in (TESTS): Add shell-test and
+       spawn-test.
+
+       * tests/spawn-test.c: (run_tests): On Win32, don't try to run
+       /bin/sh, but ipconfig (no special significance in choosing that,
+       just a program that outputs something to stdout).
+
 2000-10-15  Raja R Harinath  <harinath@cs.umn.edu>
 
        Remove need for acconfig.h, and misc. cleanups.
index 4b6ce0f..6c18f42 100644 (file)
@@ -1,3 +1,42 @@
+2000-10-16  Tor Lillqvist  <tml@iki.fi>
+
+       * giowin32.c (reader_thread): Some more debugging output.
+       (g_io_channel_win32_poll): Remove unused vars.
+
+       * gfileutils.c: Changes for Win32, with no unistd.h and no
+       S_ISLNK().
+
+       * gspawn-win32.c: Implementation of the g_spwan_* functions for
+       Win32. Due to the general non-Unixness of Win32, much of the
+       functionality that is relatively clean to implement on Unix, is
+       hard to do on Win32. We must use a separate helper program to
+       change directory, close extra file descriptors, redirect the std
+       ones, as needed, and only then start the child process. No child
+       process pid can be returned, unfortunately. Or if we used
+       CreateProcess directly, it probably could. (Now we use the spawnv*
+       functions from msvcrt.)
+
+       * glib.def: Add new entry points.
+
+       * glib.def
+       * giowin32.c: Remove g_io_channel_win32_wait_for_condition(),
+       g_io_channel_win32_poll() subsumes it.
+
+       * gbacktrace.h: G_BREAKPOINT for MSVC (on the ix86).
+
+       * gwin32.c (g_win32_getlocale): Use "sp" for
+       LANG_CROATIAN+SUBLANG_SERBIAN_LATIN.
+
+       * makefile.{mingw,msc}.in (glib_OBJECTS): Add new files.
+       Add gspawn-win32-helper.exe rule.
+
+       * tests/makefile.{mingw,msc}.in (TESTS): Add shell-test and
+       spawn-test.
+
+       * tests/spawn-test.c: (run_tests): On Win32, don't try to run
+       /bin/sh, but ipconfig (no special significance in choosing that,
+       just a program that outputs something to stdout).
+
 2000-10-15  Raja R Harinath  <harinath@cs.umn.edu>
 
        Remove need for acconfig.h, and misc. cleanups.
index 4b6ce0f..6c18f42 100644 (file)
@@ -1,3 +1,42 @@
+2000-10-16  Tor Lillqvist  <tml@iki.fi>
+
+       * giowin32.c (reader_thread): Some more debugging output.
+       (g_io_channel_win32_poll): Remove unused vars.
+
+       * gfileutils.c: Changes for Win32, with no unistd.h and no
+       S_ISLNK().
+
+       * gspawn-win32.c: Implementation of the g_spwan_* functions for
+       Win32. Due to the general non-Unixness of Win32, much of the
+       functionality that is relatively clean to implement on Unix, is
+       hard to do on Win32. We must use a separate helper program to
+       change directory, close extra file descriptors, redirect the std
+       ones, as needed, and only then start the child process. No child
+       process pid can be returned, unfortunately. Or if we used
+       CreateProcess directly, it probably could. (Now we use the spawnv*
+       functions from msvcrt.)
+
+       * glib.def: Add new entry points.
+
+       * glib.def
+       * giowin32.c: Remove g_io_channel_win32_wait_for_condition(),
+       g_io_channel_win32_poll() subsumes it.
+
+       * gbacktrace.h: G_BREAKPOINT for MSVC (on the ix86).
+
+       * gwin32.c (g_win32_getlocale): Use "sp" for
+       LANG_CROATIAN+SUBLANG_SERBIAN_LATIN.
+
+       * makefile.{mingw,msc}.in (glib_OBJECTS): Add new files.
+       Add gspawn-win32-helper.exe rule.
+
+       * tests/makefile.{mingw,msc}.in (TESTS): Add shell-test and
+       spawn-test.
+
+       * tests/spawn-test.c: (run_tests): On Win32, don't try to run
+       /bin/sh, but ipconfig (no special significance in choosing that,
+       just a program that outputs something to stdout).
+
 2000-10-15  Raja R Harinath  <harinath@cs.umn.edu>
 
        Remove need for acconfig.h, and misc. cleanups.
index 4b6ce0f..6c18f42 100644 (file)
@@ -1,3 +1,42 @@
+2000-10-16  Tor Lillqvist  <tml@iki.fi>
+
+       * giowin32.c (reader_thread): Some more debugging output.
+       (g_io_channel_win32_poll): Remove unused vars.
+
+       * gfileutils.c: Changes for Win32, with no unistd.h and no
+       S_ISLNK().
+
+       * gspawn-win32.c: Implementation of the g_spwan_* functions for
+       Win32. Due to the general non-Unixness of Win32, much of the
+       functionality that is relatively clean to implement on Unix, is
+       hard to do on Win32. We must use a separate helper program to
+       change directory, close extra file descriptors, redirect the std
+       ones, as needed, and only then start the child process. No child
+       process pid can be returned, unfortunately. Or if we used
+       CreateProcess directly, it probably could. (Now we use the spawnv*
+       functions from msvcrt.)
+
+       * glib.def: Add new entry points.
+
+       * glib.def
+       * giowin32.c: Remove g_io_channel_win32_wait_for_condition(),
+       g_io_channel_win32_poll() subsumes it.
+
+       * gbacktrace.h: G_BREAKPOINT for MSVC (on the ix86).
+
+       * gwin32.c (g_win32_getlocale): Use "sp" for
+       LANG_CROATIAN+SUBLANG_SERBIAN_LATIN.
+
+       * makefile.{mingw,msc}.in (glib_OBJECTS): Add new files.
+       Add gspawn-win32-helper.exe rule.
+
+       * tests/makefile.{mingw,msc}.in (TESTS): Add shell-test and
+       spawn-test.
+
+       * tests/spawn-test.c: (run_tests): On Win32, don't try to run
+       /bin/sh, but ipconfig (no special significance in choosing that,
+       just a program that outputs something to stdout).
+
 2000-10-15  Raja R Harinath  <harinath@cs.umn.edu>
 
        Remove need for acconfig.h, and misc. cleanups.
index 2a3a856..25d2f99 100644 (file)
@@ -48,6 +48,8 @@ void g_on_error_stack_trace (const gchar *prg_name);
  */
 #if defined (__i386__) && defined (__GNUC__) && __GNUC__ >= 2
 #  define G_BREAKPOINT()       G_STMT_START{ __asm__ __volatile__ ("int $03"); }G_STMT_END
+#elif defined (_MSC_VER) && defined (_M_IX86)
+#  define G_BREAKPOINT()       G_STMT_START{ __asm int 3h }G_STMT_END
 #elif defined (__alpha__) && defined (__GNUC__) && __GNUC__ >= 2
 #  define G_BREAKPOINT()       G_STMT_START{ __asm__ __volatile__ ("bpt"); }G_STMT_END
 #else  /* !__i386__ && !__alpha__ */
index 1176852..74b6ac7 100644 (file)
  *   Boston, MA 02111-1307, USA.
  */
 
+#include "config.h"
+
 #include "glib.h"
 
 #include <sys/stat.h>
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif
 #include <stdio.h>
 #include <string.h>
 #include <errno.h>
@@ -29,6 +33,9 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 
+#ifndef S_ISLNK
+# define S_ISLNK(x) 0
+#endif
 
 #define _(x) x
 
@@ -279,6 +286,8 @@ get_contents_stdio (const gchar *filename,
   return TRUE;  
 }
 
+#ifndef G_OS_WIN32
+
 static gboolean
 get_contents_regfile (const gchar *filename,
                       struct stat *stat_buf,
@@ -401,7 +410,8 @@ get_contents_posix (const gchar *filename,
     }
 }
 
-#ifdef G_OS_WIN32
+#else  /* G_OS_WIN32 */
+
 static gboolean
 get_contents_win32 (const gchar *filename,
                     gchar      **contents,
@@ -426,6 +436,7 @@ get_contents_win32 (const gchar *filename,
   
   return get_contents_stdio (filename, f, contents, length, error);
 }
+
 #endif
 
 /**
@@ -467,4 +478,3 @@ g_file_get_contents (const gchar *filename,
   return get_contents_posix (filename, contents, length, error);
 #endif
 }
-
index 89cdb0e..560d714 100644 (file)
@@ -201,6 +201,10 @@ reader_thread (void *parameter)
       nbytes = MIN ((channel->rdp + BUFFER_SIZE - channel->wrp - 1) % BUFFER_SIZE,
                    BUFFER_SIZE - channel->wrp);
 
+      if (channel->debug)
+       g_print ("thread %#x: calling reader for %d bytes\n",
+                channel->thread_id, nbytes);
+
       UNLOCK (channel->mutex);
 
       nbytes = (*channel->reader) (channel->fd, buffer, nbytes);
@@ -876,9 +880,7 @@ g_io_channel_win32_poll (GPollFD *fds,
                         gint     n_fds,
                         gint     timeout)
 {
-  int i;
   int result;
-  gboolean debug = FALSE;
 
   g_return_val_if_fail (n_fds >= 0, 0);
 
@@ -907,19 +909,6 @@ g_io_channel_win32_make_pollfd (GIOChannel   *channel,
       create_reader_thread (win32_channel, sock_reader);
 }
 
-gint
-g_io_channel_win32_wait_for_condition (GIOChannel  *channel,
-                                      GIOCondition condition,
-                                      gint         timeout)
-{
-  GPollFD pollfd;
-  GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-
-  g_io_channel_win32_make_pollfd (channel, condition, &pollfd);
-  
-  return g_io_channel_win32_poll (&pollfd, 1, timeout);
-}
-
 /* This variable and the functions below are present just to be 
  * binary compatible with old clients... But note that in GIMP, the
  * libgimp/gimp.c:gimp_extension_process() function will have to be modified
index db9b5aa..5ff0501 100644 (file)
--- a/glib.def
+++ b/glib.def
@@ -99,6 +99,7 @@ EXPORTS
        g_error_matches
        g_error_new
        g_error_new_literal
+       g_file_test
        g_filename_from_utf8
        g_filename_to_utf8
        g_free
@@ -162,17 +163,16 @@ EXPORTS
        g_io_channel_unix_new
        g_io_channel_unref
        g_io_channel_win32_get_fd
-       g_io_channel_win32_new_messages
+       g_io_channel_win32_make_pollfd
        g_io_channel_win32_new_fd
+       g_io_channel_win32_new_messages
        g_io_channel_win32_new_pipe
        g_io_channel_win32_new_pipe_with_wakeups
        g_io_channel_win32_new_stream_socket
-       g_io_channel_win32_make_pollfd
        g_io_channel_win32_pipe_readable
        g_io_channel_win32_pipe_request_wakeups
        g_io_channel_win32_poll
        g_io_channel_win32_set_debug
-       g_io_channel_win32_wait_for_condition
        g_io_channel_write
        g_list_alloc
        g_list_append
@@ -268,8 +268,8 @@ EXPORTS
        g_pipe_readable_msg
        g_print
        g_printerr
-       g_propagate_error
        g_printf_string_upper_bound
+       g_propagate_error
        g_ptr_array_add
        g_ptr_array_free
        g_ptr_array_new
@@ -350,6 +350,9 @@ EXPORTS
        g_set_print_handler
        g_set_printerr_handler
        g_set_warning_handler
+       g_shell_parse_argv
+       g_shell_quote
+       g_shell_unquote
        g_slist_alloc
        g_slist_append
        g_slist_concat
@@ -379,6 +382,12 @@ EXPORTS
        g_source_remove_by_source_data
        g_source_remove_by_user_data
        g_spaced_primes_closest
+       g_spawn_async
+       g_spawn_async_with_pipes
+       g_spawn_command_line_async
+       g_spawn_command_line_sync
+       g_spawn_error_quark
+       g_spawn_sync
        g_static_mutex_get_mutex_impl
        g_static_private_get
        g_static_private_set
index 2a3a856..25d2f99 100644 (file)
@@ -48,6 +48,8 @@ void g_on_error_stack_trace (const gchar *prg_name);
  */
 #if defined (__i386__) && defined (__GNUC__) && __GNUC__ >= 2
 #  define G_BREAKPOINT()       G_STMT_START{ __asm__ __volatile__ ("int $03"); }G_STMT_END
+#elif defined (_MSC_VER) && defined (_M_IX86)
+#  define G_BREAKPOINT()       G_STMT_START{ __asm int 3h }G_STMT_END
 #elif defined (__alpha__) && defined (__GNUC__) && __GNUC__ >= 2
 #  define G_BREAKPOINT()       G_STMT_START{ __asm__ __volatile__ ("bpt"); }G_STMT_END
 #else  /* !__i386__ && !__alpha__ */
index 1176852..74b6ac7 100644 (file)
  *   Boston, MA 02111-1307, USA.
  */
 
+#include "config.h"
+
 #include "glib.h"
 
 #include <sys/stat.h>
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif
 #include <stdio.h>
 #include <string.h>
 #include <errno.h>
@@ -29,6 +33,9 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 
+#ifndef S_ISLNK
+# define S_ISLNK(x) 0
+#endif
 
 #define _(x) x
 
@@ -279,6 +286,8 @@ get_contents_stdio (const gchar *filename,
   return TRUE;  
 }
 
+#ifndef G_OS_WIN32
+
 static gboolean
 get_contents_regfile (const gchar *filename,
                       struct stat *stat_buf,
@@ -401,7 +410,8 @@ get_contents_posix (const gchar *filename,
     }
 }
 
-#ifdef G_OS_WIN32
+#else  /* G_OS_WIN32 */
+
 static gboolean
 get_contents_win32 (const gchar *filename,
                     gchar      **contents,
@@ -426,6 +436,7 @@ get_contents_win32 (const gchar *filename,
   
   return get_contents_stdio (filename, f, contents, length, error);
 }
+
 #endif
 
 /**
@@ -467,4 +478,3 @@ g_file_get_contents (const gchar *filename,
   return get_contents_posix (filename, contents, length, error);
 #endif
 }
-
index 89cdb0e..560d714 100644 (file)
@@ -201,6 +201,10 @@ reader_thread (void *parameter)
       nbytes = MIN ((channel->rdp + BUFFER_SIZE - channel->wrp - 1) % BUFFER_SIZE,
                    BUFFER_SIZE - channel->wrp);
 
+      if (channel->debug)
+       g_print ("thread %#x: calling reader for %d bytes\n",
+                channel->thread_id, nbytes);
+
       UNLOCK (channel->mutex);
 
       nbytes = (*channel->reader) (channel->fd, buffer, nbytes);
@@ -876,9 +880,7 @@ g_io_channel_win32_poll (GPollFD *fds,
                         gint     n_fds,
                         gint     timeout)
 {
-  int i;
   int result;
-  gboolean debug = FALSE;
 
   g_return_val_if_fail (n_fds >= 0, 0);
 
@@ -907,19 +909,6 @@ g_io_channel_win32_make_pollfd (GIOChannel   *channel,
       create_reader_thread (win32_channel, sock_reader);
 }
 
-gint
-g_io_channel_win32_wait_for_condition (GIOChannel  *channel,
-                                      GIOCondition condition,
-                                      gint         timeout)
-{
-  GPollFD pollfd;
-  GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
-
-  g_io_channel_win32_make_pollfd (channel, condition, &pollfd);
-  
-  return g_io_channel_win32_poll (&pollfd, 1, timeout);
-}
-
 /* This variable and the functions below are present just to be 
  * binary compatible with old clients... But note that in GIMP, the
  * libgimp/gimp.c:gimp_extension_process() function will have to be modified
index db9b5aa..5ff0501 100644 (file)
@@ -99,6 +99,7 @@ EXPORTS
        g_error_matches
        g_error_new
        g_error_new_literal
+       g_file_test
        g_filename_from_utf8
        g_filename_to_utf8
        g_free
@@ -162,17 +163,16 @@ EXPORTS
        g_io_channel_unix_new
        g_io_channel_unref
        g_io_channel_win32_get_fd
-       g_io_channel_win32_new_messages
+       g_io_channel_win32_make_pollfd
        g_io_channel_win32_new_fd
+       g_io_channel_win32_new_messages
        g_io_channel_win32_new_pipe
        g_io_channel_win32_new_pipe_with_wakeups
        g_io_channel_win32_new_stream_socket
-       g_io_channel_win32_make_pollfd
        g_io_channel_win32_pipe_readable
        g_io_channel_win32_pipe_request_wakeups
        g_io_channel_win32_poll
        g_io_channel_win32_set_debug
-       g_io_channel_win32_wait_for_condition
        g_io_channel_write
        g_list_alloc
        g_list_append
@@ -268,8 +268,8 @@ EXPORTS
        g_pipe_readable_msg
        g_print
        g_printerr
-       g_propagate_error
        g_printf_string_upper_bound
+       g_propagate_error
        g_ptr_array_add
        g_ptr_array_free
        g_ptr_array_new
@@ -350,6 +350,9 @@ EXPORTS
        g_set_print_handler
        g_set_printerr_handler
        g_set_warning_handler
+       g_shell_parse_argv
+       g_shell_quote
+       g_shell_unquote
        g_slist_alloc
        g_slist_append
        g_slist_concat
@@ -379,6 +382,12 @@ EXPORTS
        g_source_remove_by_source_data
        g_source_remove_by_user_data
        g_spaced_primes_closest
+       g_spawn_async
+       g_spawn_async_with_pipes
+       g_spawn_command_line_async
+       g_spawn_command_line_sync
+       g_spawn_error_quark
+       g_spawn_sync
        g_static_mutex_get_mutex_impl
        g_static_private_get
        g_static_private_set
diff --git a/glib/gspawn-win32.c b/glib/gspawn-win32.c
new file mode 100644 (file)
index 0000000..11779da
--- /dev/null
@@ -0,0 +1,1191 @@
+/* gspawn.c - Process launching
+ *
+ *  Copyright 2000 Red Hat, Inc.
+ *
+ * GLib is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * GLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GLib; see the file COPYING.LIB.  If not, write
+ * to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Implementation details on Win32.
+ *
+ * - There is no way to set the no-inherit flag for
+ *   a "file descriptor" in the MS C runtime. The flag is there,
+ *   and the dospawn() function uses it, but unfortunately
+ *   this flag can only be set when opening the file.
+ * - As there is no fork(), we cannot reliably change directory
+ *   before starting the child process. (There might be several threads
+ *   running, and the current directory is common for all threads.)
+ *
+ * Thus, we must in most cases use a helper program to handle closing
+ * of (inherited) file descriptors and changing of directory. In fact,
+ * we do it all the time.
+ *
+ * This source file contains the source for that helper program.
+ * To compile it, #define GSPAWN_HELPER.
+ */
+
+/* Define this to get some logging all the time */
+/* #define G_SPAWN_WIN32_DEBUG */
+
+#include "glib.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <windows.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <io.h>
+#include <process.h>
+
+#ifdef _
+#warning "FIXME remove gettext hack"
+#endif
+
+#define _(x) x
+
+#ifdef G_SPAWN_WIN32_DEBUG
+  static int debug = 1;
+  #define SETUP_DEBUG() /* empty */
+
+#else
+  static int debug = -1;
+  #define SETUP_DEBUG()                                        \
+    G_STMT_START                                       \
+      {                                                        \
+       if (debug == -1)                                \
+         if (getenv ("G_SPAWN_WIN32_DEBUG") != NULL)   \
+           debug = 1;                                  \
+         else                                          \
+           debug = 0;                                  \
+      }                                                        \
+    G_STMT_END
+#endif
+
+enum
+{
+  CHILD_NO_ERROR,
+  CHILD_CHDIR_FAILED,
+  CHILD_SPAWN_FAILED,
+};
+
+enum {
+  ARG_CHILD_ERR_REPORT = 1,
+  ARG_STDIN,
+  ARG_STDOUT,
+  ARG_STDERR,
+  ARG_WORKING_DIRECTORY,
+  ARG_CLOSE_DESCRIPTORS,
+  ARG_USE_PATH,
+  ARG_WAIT,
+  ARG_PROGRAM,
+  ARG_COUNT = ARG_PROGRAM
+};
+
+#ifndef GSPAWN_HELPER
+
+static gboolean make_pipe            (gint                  p[2],
+                                      GError              **error);
+static gboolean fork_exec_with_pipes (gboolean              dont_wait,
+                                     const gchar          *working_directory,
+                                      gchar               **argv,
+                                      gchar               **envp,
+                                      gboolean              close_descriptors,
+                                      gboolean              search_path,
+                                      gboolean              stdout_to_null,
+                                      gboolean              stderr_to_null,
+                                      gboolean              child_inherits_stdin,
+                                      GSpawnChildSetupFunc  child_setup,
+                                      gpointer              user_data,
+                                      gint                 *standard_input,
+                                      gint                 *standard_output,
+                                      gint                 *standard_error,
+                                     gint                 *exit_status,
+                                      GError              **error);
+
+GQuark
+g_spawn_error_quark (void)
+{
+  static GQuark quark = 0;
+  if (quark == 0)
+    quark = g_quark_from_static_string ("g-exec-error-quark");
+  return quark;
+}
+
+/**
+ * g_spawn_async:
+ * @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
+ * @flags: flags from #GSpawnFlags
+ * @child_setup: function to run in the child just before exec()
+ * @user_data: user data for @child_setup
+ * @child_pid: return location for child process ID, or NULL
+ * @error: return location for error
+ * 
+ * See g_spawn_async_with_pipes() for a full description; this function
+ * simply calls the g_spawn_async_with_pipes() without any pipes.
+ * 
+ * Return value: TRUE on success, FALSE if error is set
+ **/
+gboolean
+g_spawn_async (const gchar          *working_directory,
+               gchar               **argv,
+               gchar               **envp,
+               GSpawnFlags           flags,
+               GSpawnChildSetupFunc  child_setup,
+               gpointer              user_data,
+               gint                 *child_pid,
+               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_pid,
+                                   NULL, NULL, NULL,
+                                   error);
+}
+
+/* Avoids a danger in threaded situations (calling close()
+ * on a file descriptor twice, and another thread has
+ * re-opened it since the first close)
+ */
+static gint
+close_and_invalidate (gint *fd)
+{
+  gint ret;
+
+  ret = close (*fd);
+  *fd = -1;
+
+  return ret;
+}
+
+typedef enum
+{
+  READ_FAILED = 0, /* FALSE */
+  READ_OK,
+  READ_EOF
+} ReadResult;
+
+static ReadResult
+read_data (GString     *str,
+           GIOChannel  *iochannel,
+           GError     **error)
+{
+  GIOError gioerror;
+  gint bytes;
+  gchar buf[4096];
+
+ again:
+  
+  gioerror = g_io_channel_read (iochannel, buf, sizeof (buf), &bytes);
+
+  if (bytes == 0)
+    return READ_EOF;
+  else if (bytes > 0)
+    {
+      g_string_append_len (str, buf, bytes);
+      return READ_OK;
+    }
+  else if (gioerror == G_IO_ERROR_AGAIN)
+    goto again;
+  else if (gioerror != G_IO_ERROR_NONE)
+    {
+      g_set_error (error,
+                   G_SPAWN_ERROR,
+                   G_SPAWN_ERROR_READ,
+                   _("Failed to read data from child process"));
+      
+      return READ_FAILED;
+    }
+  else
+    return READ_OK;
+}
+
+/**
+ * g_spawn_sync:
+ * @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
+ * @flags: flags from #GSpawnFlags
+ * @child_setup: function to run in the child just before exec()
+ * @user_data: user data for @child_setup
+ * @standard_output: return location for child output 
+ * @standard_error: return location for child error messages
+ * @exit_status: child exit status, as returned by waitpid()
+ * @error: return location for error
+ *
+ * Executes a child synchronously (waits for the child to exit before returning).
+ * All output from the child is stored in @standard_output and @standard_error,
+ * if those parameters are non-NULL. If @exit_status is non-NULL, the exit status
+ * of the child is stored there as it would be by waitpid(); standard UNIX
+ * macros such as WIFEXITED() and WEXITSTATUS() 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.
+ * 
+ * Return value: TRUE on success, FALSE if an error was set.
+ **/
+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)     
+{
+  gint outpipe = -1;
+  gint errpipe = -1;
+  GIOChannel *outchannel = NULL;
+  GIOChannel *errchannel = NULL;
+  GPollFD outfd, errfd;
+  GPollFD fds[2];
+  gint nfds;
+  gint outindex = -1;
+  gint errindex = -1;
+  gint ret;
+  GString *outstr = NULL;
+  GString *errstr = NULL;
+  gboolean failed;
+  gint status;
+  
+  g_return_val_if_fail (argv != NULL, FALSE);
+  g_return_val_if_fail (!(flags & G_SPAWN_DO_NOT_REAP_CHILD), FALSE);
+  g_return_val_if_fail (standard_output == NULL ||
+                        !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
+  g_return_val_if_fail (standard_error == NULL ||
+                        !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
+  
+  /* Just to ensure segfaults if callers try to use
+   * these when an error is reported.
+   */
+  if (standard_output)
+    *standard_output = NULL;
+
+  if (standard_error)
+    *standard_error = NULL;
+  
+  if (!fork_exec_with_pipes (FALSE,
+                             working_directory,
+                             argv,
+                             envp,
+                             !(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN),
+                             (flags & G_SPAWN_SEARCH_PATH) != 0,
+                             (flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0,
+                             (flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0,
+                             (flags & G_SPAWN_CHILD_INHERITS_STDIN) != 0,
+                             child_setup,
+                             user_data,
+                             NULL,
+                             standard_output ? &outpipe : NULL,
+                             standard_error ? &errpipe : NULL,
+                            &status,
+                             error))
+    return FALSE;
+
+  /* Read data from child. */
+  
+  failed = FALSE;
+
+  if (outpipe >= 0)
+    {
+      outstr = g_string_new ("");
+      outchannel = g_io_channel_win32_new_fd (outpipe);
+      g_io_channel_win32_make_pollfd (outchannel,
+                                     G_IO_IN | G_IO_ERR | G_IO_HUP,
+                                     &outfd);
+    }
+      
+  if (errpipe >= 0)
+    {
+      errstr = g_string_new ("");
+      errchannel = g_io_channel_win32_new_fd (errpipe);
+      g_io_channel_win32_make_pollfd (errchannel,
+                                     G_IO_IN | G_IO_ERR | G_IO_HUP,
+                                     &errfd);
+    }
+
+  /* Read data until we get EOF on both pipes. */
+  while (!failed &&
+         (outpipe >= 0 ||
+          errpipe >= 0))
+    {
+      nfds = 0;
+      if (outpipe >= 0)
+       {
+         fds[nfds] = outfd;
+         outindex = nfds;
+         nfds++;
+       }
+      if (errpipe >= 0)
+       {
+         fds[nfds] = errfd;
+         errindex = nfds;
+         nfds++;
+       }
+
+      if (debug)
+       g_print ("%s:g_spawn_sync: calling g_io_channel_win32_poll, nfds=%d\n",
+                __FILE__, nfds);
+
+      ret = g_io_channel_win32_poll (fds, nfds, -1);
+
+      if (ret < 0)
+        {
+          failed = TRUE;
+
+          g_set_error (error,
+                       G_SPAWN_ERROR,
+                       G_SPAWN_ERROR_READ,
+                       _("Unexpected error in g_io_channel_win32_poll() reading data from a child process"));
+              
+          break;
+        }
+
+      if (outpipe >= 0 && (fds[outindex].revents & G_IO_IN))
+        {
+          switch (read_data (outstr, outchannel, error))
+            {
+            case READ_FAILED:
+             if (debug)
+               g_print ("g_spawn_sync: outchannel: READ_FAILED\n");
+              failed = TRUE;
+              break;
+            case READ_EOF:
+             if (debug)
+               g_print ("g_spawn_sync: outchannel: READ_EOF\n");
+              g_io_channel_unref (outchannel);
+             outchannel = NULL;
+              close_and_invalidate (&outpipe);
+              break;
+            default:
+             if (debug)
+               g_print ("g_spawn_sync: outchannel: OK\n");
+              break;
+            }
+
+          if (failed)
+            break;
+        }
+
+      if (errpipe >= 0 && (fds[errindex].revents & G_IO_IN))
+        {
+          switch (read_data (errstr, errchannel, error))
+            {
+            case READ_FAILED:
+             if (debug)
+               g_print ("g_spawn_sync: errchannel: READ_FAILED\n");
+              failed = TRUE;
+              break;
+            case READ_EOF:
+             if (debug)
+               g_print ("g_spawn_sync: errchannel: READ_EOF\n");
+             g_io_channel_unref (errchannel);
+             errchannel = NULL;
+              close_and_invalidate (&errpipe);
+              break;
+            default:
+             if (debug)
+               g_print ("g_spawn_sync: errchannel: OK\n");
+              break;
+            }
+
+          if (failed)
+            break;
+        }
+    }
+
+  /* These should only be open still if we had an error.  */
+  
+  if (outchannel != NULL)
+    g_io_channel_unref (outchannel);
+  if (errchannel != NULL)
+    g_io_channel_unref (errchannel);
+  if (outpipe >= 0)
+    close_and_invalidate (&outpipe);
+  if (errpipe >= 0)
+    close_and_invalidate (&errpipe);
+  
+  if (failed)
+    {
+      if (outstr)
+        g_string_free (outstr, TRUE);
+      if (errstr)
+        g_string_free (errstr, TRUE);
+
+      return FALSE;
+    }
+  else
+    {
+      if (exit_status)
+        *exit_status = status;
+      
+      if (standard_output)        
+        *standard_output = g_string_free (outstr, FALSE);
+
+      if (standard_error)
+        *standard_error = g_string_free (errstr, FALSE);
+
+      return TRUE;
+    }
+}
+
+/**
+ * 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
+ * @flags: flags from #GSpawnFlags
+ * @child_setup: function to run in the child just before exec()
+ * @user_data: user data for @child_setup
+ * @child_pid: return location for child process ID, or NULL
+ * @standard_input: return location for file descriptor to write to child's stdin, or NULL
+ * @standard_output: return location for file descriptor to read child's stdout, or NULL
+ * @standard_error: return location for file descriptor to read child's stderr, or NULL
+ * @error: return location for error
+ *
+ * Executes a child program asynchronously (your program will not
+ * block waiting for the child to exit). The child program is
+ * specified by the only argument that must be provided, @argv. @argv
+ * should be a NULL-terminated array of strings, to be passed as the
+ * argument vector for the child. The first string in @argv is of
+ * course the name of the program to execute. By default, the name of
+ * the program must be a full path; the PATH shell variable will only
+ * be searched if you pass the %G_SPAWN_SEARCH_PATH flag.
+ *
+ * @envp is a NULL-terminated array of strings, where each string
+ * has the form <literal>KEY=VALUE</literal>. This will become
+ * the child's environment. If @envp is NULL, the child inherits its
+ * parent's environment.
+ *
+ * @flags should be the bitwise OR of any flags you want to affect the
+ * function's behavior. The %G_SPAWN_DO_NOT_REAP_CHILD means that the
+ * child will not be automatically reaped; you must call waitpid() or
+ * handle SIGCHLD yourself, or the child will become a zombie.
+ * %G_SPAWN_LEAVE_DESCRIPTORS_OPEN means that the parent's open file
+ * descriptors will be inherited by the child; otherwise all
+ * descriptors except stdin/stdout/stderr will be closed before
+ * calling exec() in the child. %G_SPAWN_SEARCH_PATH means that
+ * <literal>argv[0]</literal> need not be an absolute path, it
+ * will be looked for in the user's PATH. %G_SPAWN_STDOUT_TO_DEV_NULL
+ * means that the child's standad output will be discarded, instead
+ * of going to the same location as the parent's standard output.
+ * %G_SPAWN_STDERR_TO_DEV_NULL means that the child's standard error
+ * will be discarded. %G_SPAWN_CHILD_INHERITS_STDIN means that
+ * the child will inherit the parent's standard input (by default,
+ * the child's standard input is attached to /dev/null).
+ *
+ * @child_setup and @user_data are a function and user data to be
+ * called in the child after GLib has performed all the setup it plans
+ * to perform (including creating pipes, closing file descriptors,
+ * etc.) but before calling exec(). That is, @child_setup is called
+ * just before calling exec() in the child. Obviously actions taken in
+ * this function will only affect the child, not the parent. 
+ *
+ * If non-NULL, @child_pid will be filled with the child's process
+ * ID. You can use the process ID to send signals to the child, or
+ * to waitpid() if you specified the %G_SPAWN_DO_NOT_REAP_CHILD flag.
+ *
+ * If non-NULL, the @standard_input, @standard_output, @standard_error
+ * locations will be filled with file descriptors for writing to the child's
+ * standard input or reading from its standard output or standard error.
+ * The caller of g_spawn_async_with_pipes() must close these file descriptors
+ * when they are no longer in use. If these parameters are NULL, the
+ * corresponding pipe won't be created.
+ *
+ * @error can be NULL to ignore errors, or non-NULL to report errors.
+ * If an error is set, the function returns FALSE. Errors
+ * are reported even if they occur in the child (for example if the
+ * executable in <literal>argv[0]</literal> is not found). Typically
+ * the <literal>message</literal> field of returned errors should be displayed
+ * to users. Possible errors are those from the #G_SPAWN_ERROR domain.
+ *
+ * If an error occurs, @child_pid, @standard_input, @standard_output,
+ * and @standard_error will not be filled with valid values.
+ * 
+ * Return value: TRUE on success, FALSE if an error was set
+ **/
+gboolean
+g_spawn_async_with_pipes (const gchar          *working_directory,
+                          gchar               **argv,
+                          gchar               **envp,
+                          GSpawnFlags           flags,
+                          GSpawnChildSetupFunc  child_setup,
+                          gpointer              user_data,
+                          gint                 *child_pid,
+                          gint                 *standard_input,
+                          gint                 *standard_output,
+                          gint                 *standard_error,
+                          GError              **error)
+{
+  g_return_val_if_fail (argv != NULL, FALSE);
+  g_return_val_if_fail (standard_output == NULL ||
+                        !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
+  g_return_val_if_fail (standard_error == NULL ||
+                        !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
+  /* can't inherit stdin if we have an input pipe. */
+  g_return_val_if_fail (standard_input == NULL ||
+                        !(flags & G_SPAWN_CHILD_INHERITS_STDIN), FALSE);
+  
+  return fork_exec_with_pipes (!(flags & G_SPAWN_DO_NOT_REAP_CHILD),
+                               working_directory,
+                               argv,
+                               envp,
+                               !(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN),
+                               (flags & G_SPAWN_SEARCH_PATH) != 0,
+                               (flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0,
+                               (flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0,
+                               (flags & G_SPAWN_CHILD_INHERITS_STDIN) != 0,
+                               child_setup,
+                               user_data,
+                               standard_input,
+                               standard_output,
+                               standard_error,
+                              NULL,
+                               error);
+}
+
+/**
+ * g_spawn_command_line_sync:
+ * @command_line: a command line 
+ * @standard_output: return location for child output
+ * @standard_error: return location for child errors
+ * @exit_status: return location for child exit status
+ * @error: return location for errors
+ *
+ * A simple version of g_spawn_sync() with little-used parameters
+ * removed, taking a command line instead of an argument vector.  See
+ * g_spawn_sync() for full details. @command_line will be parsed by
+ * g_shell_parse_argv(). Unlike g_spawn_sync(), the %G_SPAWN_SEARCH_PATH flag
+ * is enabled. Note that %G_SPAWN_SEARCH_PATH can have security
+ * implications, so consider using g_spawn_sync() directly if
+ * appropriate. Possible errors are those from g_spawn_sync() and those
+ * from g_shell_parse_argv().
+ * 
+ * Return value: TRUE on success, FALSE if an error was set
+ **/
+gboolean
+g_spawn_command_line_sync (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 (NULL,
+                         argv,
+                         NULL,
+                         G_SPAWN_SEARCH_PATH,
+                         NULL,
+                         NULL,
+                         standard_output,
+                         standard_error,
+                         exit_status,
+                         error);
+  g_strfreev (argv);
+
+  return retval;
+}
+
+/**
+ * g_spawn_command_line_async:
+ * @command_line: a command line
+ * @error: return location for errors
+ * 
+ * A simple version of g_spawn_async() that parses a command line with
+ * g_shell_parse_argv() and passes it to g_spawn_async(). Runs a
+ * command line in the background. Unlike g_spawn_async(), the
+ * %G_SPAWN_SEARCH_PATH flag is enabled, other flags are not. Note
+ * that %G_SPAWN_SEARCH_PATH can have security implications, so
+ * consider using g_spawn_async() directly if appropriate. Possible
+ * errors are those from g_shell_parse_argv() and g_spawn_async().
+ * 
+ * Return value: TRUE on success, FALSE if error is set.
+ **/
+gboolean
+g_spawn_command_line_async (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 (NULL,
+                          argv,
+                          NULL,
+                          G_SPAWN_SEARCH_PATH,
+                          NULL,
+                          NULL,
+                          NULL,
+                          error);
+  g_strfreev (argv);
+
+  return retval;
+}
+
+static gint
+do_exec (gboolean              dont_wait,
+        gint                  child_err_report_fd,
+         gint                  stdin_fd,
+         gint                  stdout_fd,
+         gint                  stderr_fd,
+         const gchar          *working_directory,
+         gchar               **argv,
+         gchar               **envp,
+         gboolean              close_descriptors,
+         gboolean              search_path,
+         gboolean              stdout_to_null,
+         gboolean              stderr_to_null,
+         gboolean              child_inherits_stdin,
+         GSpawnChildSetupFunc  child_setup,
+         gpointer              user_data)
+{
+  gchar **new_argv;
+  gchar args[ARG_COUNT][10];
+  gint i;
+  int argc = 0;
+
+  SETUP_DEBUG();
+
+  while (argv[argc])
+    ++argc;
+
+  new_argv = g_new (gchar *, argc + 1 + ARG_COUNT);
+
+  new_argv[0] = "gspawn-win32-helper";
+  sprintf (args[ARG_CHILD_ERR_REPORT], "%d", child_err_report_fd);
+  new_argv[ARG_CHILD_ERR_REPORT] = args[ARG_CHILD_ERR_REPORT];
+
+  if (stdin_fd >= 0)
+    {
+      sprintf (args[ARG_STDIN], "%d", stdin_fd);
+      new_argv[ARG_STDIN] = args[ARG_STDIN];
+    }
+  else if (child_inherits_stdin)
+    {
+      /* Let stdin be alone */
+      new_argv[ARG_STDIN] = "-";
+    }
+  else
+    {
+      /* Keep process from blocking on a read of stdin */
+      new_argv[ARG_STDIN] = "z";
+    }
+
+  if (stdout_fd >= 0)
+    {
+      sprintf (args[ARG_STDOUT], "%d", stdout_fd);
+      new_argv[ARG_STDOUT] = args[ARG_STDOUT];
+    }
+  else if (stdout_to_null)
+    {
+      new_argv[ARG_STDOUT] = "z";
+    }
+  else
+    {
+      new_argv[ARG_STDOUT] = "-";
+    }
+
+  if (stderr_fd >= 0)
+    {
+      sprintf (args[ARG_STDERR], "%d", stderr_fd);
+      new_argv[ARG_STDERR] = args[ARG_STDERR];
+    }
+  else if (stderr_to_null)
+    {
+      new_argv[ARG_STDERR] = "z";
+    }
+  else
+    {
+      new_argv[ARG_STDERR] = "-";
+    }
+
+  if (working_directory && *working_directory)
+    new_argv[ARG_WORKING_DIRECTORY] = working_directory;
+  else
+    new_argv[ARG_WORKING_DIRECTORY] = "-";
+
+  if (close_descriptors)
+    new_argv[ARG_CLOSE_DESCRIPTORS] = "y";
+  else
+    new_argv[ARG_CLOSE_DESCRIPTORS] = "-";
+
+  if (search_path)
+    new_argv[ARG_USE_PATH] = "y";
+  else
+    new_argv[ARG_USE_PATH] = "-";
+
+  if (dont_wait)
+    new_argv[ARG_WAIT] = "-";
+  else
+    new_argv[ARG_WAIT] = "w";
+
+  for (i = 0; i <= argc; i++)
+    new_argv[ARG_PROGRAM + i] = argv[i];
+
+  /* Call user function just before we execute the helper program,
+   * which executes the program. Dunno what's the usefulness of this.
+   * A child setup function used on Unix probably isn't of much use
+   * as such on Win32, anyhow.
+   */
+  if (child_setup)
+    {
+      (* child_setup) (user_data);
+    }
+
+  if (debug)
+    {
+      g_print ("calling gspawn-win32-helper with argv:\n");
+      for (i = 0; i < argc + 1 + ARG_COUNT; i++)
+       g_print ("argv[%d]: %s\n", i, (new_argv[i] ? new_argv[i] : "NULL"));
+    }
+  
+  if (envp != NULL)
+    /* Let's hope envp hasn't mucked with PATH so that
+     * gspawn-win32-helper.exe isn't found.
+     */
+    spawnvpe (P_NOWAIT, "gspawn-win32-helper", new_argv, envp);
+  else
+    spawnvp (P_NOWAIT, "gspawn-win32-helper", new_argv);
+
+  /* FIXME: What if gspawn-win32-helper.exe isn't found? */
+
+  /* Close the child_err_report_fd and the other process's ends of the
+   * pipes in this process, otherwise the reader will never get
+   * EOF.
+   */
+  close (child_err_report_fd);
+  if (stdin_fd >= 0)
+    close (stdin_fd);
+  if (stdout_fd >= 0)
+    close (stdout_fd);
+  if (stderr_fd >= 0)
+    close (stderr_fd);
+
+  g_free (new_argv);
+
+  return 0;
+}
+
+static gboolean
+read_ints (int      fd,
+           gint*    buf,
+           gint     n_ints_in_buf,
+           gint    *n_ints_read,
+           GError **error)
+{
+  gint bytes = 0;
+  
+  while (bytes < sizeof(gint)*n_ints_in_buf)
+    {
+      gint chunk;
+
+      if (debug)
+       g_print ("%s:read_ints: trying to read %d bytes from pipe...\n",
+                __FILE__,
+                sizeof(gint)*n_ints_in_buf - bytes);
+
+      chunk = read (fd, ((gchar*)buf) + bytes,
+                   sizeof(gint)*n_ints_in_buf - bytes);
+
+      if (debug)
+       g_print ("... got %d bytes\n", chunk);
+          
+      if (chunk < 0)
+        {
+          /* Some weird shit happened, bail out */
+              
+          g_set_error (error,
+                       G_SPAWN_ERROR,
+                       G_SPAWN_ERROR_FAILED,
+                       _("Failed to read from child pipe (%s)"),
+                       g_strerror (errno));
+
+          return FALSE;
+        }
+      else if (chunk == 0)
+        break; /* EOF */
+      else
+       bytes += chunk;
+    }
+
+  *n_ints_read = bytes/sizeof(gint);
+
+  return TRUE;
+}
+
+static gboolean
+fork_exec_with_pipes (gboolean              dont_wait,
+                      const gchar          *working_directory,
+                      gchar               **argv,
+                      gchar               **envp,
+                      gboolean              close_descriptors,
+                      gboolean              search_path,
+                      gboolean              stdout_to_null,
+                      gboolean              stderr_to_null,
+                     gboolean              child_inherits_stdin,
+                      GSpawnChildSetupFunc  child_setup,
+                      gpointer              user_data,
+                      gint                 *standard_input,
+                      gint                 *standard_output,
+                      gint                 *standard_error,
+                     gint                 *exit_status,
+                      GError              **error)     
+{
+  gint stdin_pipe[2] = { -1, -1 };
+  gint stdout_pipe[2] = { -1, -1 };
+  gint stderr_pipe[2] = { -1, -1 };
+  gint child_err_report_pipe[2] = { -1, -1 };
+  gint status;
+  gint bytes;
+  gint buf[2];
+  gint n_ints = 0;
+  
+  if (!make_pipe (child_err_report_pipe, error))
+    return FALSE;
+
+  if (standard_input && !make_pipe (stdin_pipe, error))
+    goto cleanup_and_fail;
+  
+  if (standard_output && !make_pipe (stdout_pipe, error))
+    goto cleanup_and_fail;
+
+  if (standard_error && !make_pipe (stderr_pipe, error))
+    goto cleanup_and_fail;
+
+  status = do_exec (dont_wait,
+                   child_err_report_pipe[1],
+                   stdin_pipe[0],
+                   stdout_pipe[1],
+                   stderr_pipe[1],
+                   working_directory,
+                   argv,
+                   envp,
+                   close_descriptors,
+                   search_path,
+                   stdout_to_null,
+                   stderr_to_null,
+                   child_inherits_stdin,
+                   child_setup,
+                   user_data);
+      
+  if (!read_ints (child_err_report_pipe[0],
+                 buf, 2, &n_ints,
+                 error))
+    goto cleanup_and_fail;
+        
+  if (n_ints == 2)
+    {
+      /* Error from the child. */
+      
+      switch (buf[0])
+       {
+       case CHILD_NO_ERROR:
+         break;
+         
+       case CHILD_CHDIR_FAILED:
+         g_set_error (error,
+                      G_SPAWN_ERROR,
+                      G_SPAWN_ERROR_CHDIR,
+                      _("Failed to change to directory '%s' (%s)"),
+                      working_directory,
+                      g_strerror (buf[1]));
+         goto cleanup_and_fail;
+         
+       case CHILD_SPAWN_FAILED:
+         g_set_error (error,
+                      G_SPAWN_ERROR,
+                      G_SPAWN_ERROR_FAILED,
+                      _("Failed to execute child process (%s)"),
+                      g_strerror (buf[1]));
+         goto cleanup_and_fail;
+       }
+    }
+
+  /* Success against all odds! return the information */
+      
+  if (standard_input)
+    *standard_input = stdin_pipe[1];
+  if (standard_output)
+    *standard_output = stdout_pipe[0];
+  if (standard_error)
+    *standard_error = stderr_pipe[0];
+  if (exit_status)
+    *exit_status = status;
+  
+  return TRUE;
+
+ cleanup_and_fail:
+  close_and_invalidate (&child_err_report_pipe[0]);
+  close_and_invalidate (&child_err_report_pipe[1]);
+  close_and_invalidate (&stdin_pipe[0]);
+  close_and_invalidate (&stdin_pipe[1]);
+  close_and_invalidate (&stdout_pipe[0]);
+  close_and_invalidate (&stdout_pipe[1]);
+  close_and_invalidate (&stderr_pipe[0]);
+  close_and_invalidate (&stderr_pipe[1]);
+
+  return FALSE;
+}
+
+static gboolean
+make_pipe (gint     p[2],
+           GError **error)
+{
+  if (pipe (p) < 0)
+    {
+      g_set_error (error,
+                   G_SPAWN_ERROR,
+                   G_SPAWN_ERROR_FAILED,
+                   _("Failed to create pipe for communicating with child process (%s)"),
+                   g_strerror (errno));
+      return FALSE;
+    }
+  else
+    return TRUE;
+}
+
+#else /* GSPAWN_HELPER */
+
+static void
+write_err_and_exit (gint fd,
+                   gint msg)
+{
+  gint en = errno;
+  
+  write (fd, &msg, sizeof(msg));
+  write (fd, &en, sizeof(en));
+  
+  _exit (1);
+}
+
+static void
+write_no_error (gint fd)
+{
+  gint msg = CHILD_NO_ERROR;
+  gint en = 0;
+
+  write (fd, &msg, sizeof(msg));
+  write (fd, &en, sizeof(en));
+}
+
+#ifdef __GNUC__
+#  ifndef _stdcall
+#    define _stdcall  __attribute__((stdcall))
+#  endif
+#endif
+
+/* We build gspawn-win32-helper.exe as a Windows GUI application
+ * to avoid any temporarily flashing console windows in case
+ * the gspawn function is invoked by a GUI program. Thus, no main()
+ * but a WinMain(). We do, however, still use argc and argv tucked
+ * away in the global __argc and __argv by the C runtime startup code.
+ */
+
+int _stdcall
+WinMain (struct HINSTANCE__ *hInstance,
+        struct HINSTANCE__ *hPrevInstance,
+        char               *lpszCmdLine,
+        int                 nCmdShow)
+{
+  int child_err_report_fd;
+  int i;
+  int fd;
+  int mode;
+  GString *debugstring;
+
+  SETUP_DEBUG();
+
+  if (debug)
+    {
+      debugstring = g_string_new ("");
+
+      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);
+
+  /* argv[ARG_CHILD_ERR_REPORT] is the file descriptor onto which
+   * write error messages.
+   */
+  child_err_report_fd = atoi (__argv[ARG_CHILD_ERR_REPORT]);
+
+  /* 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:.
+   */
+  if (__argv[ARG_STDIN][0] == '-')
+    ; /* Nothing */
+  else if (__argv[ARG_STDIN][0] == 'z')
+    {
+      fd = open ("NUL:", O_RDONLY);
+      if (fd != 0)
+       {
+         dup2 (fd, 0);
+         close (fd);
+       }
+    }
+  else
+    {
+      fd = atoi (__argv[ARG_STDIN]);
+      if (fd != 0)
+       {
+         dup2 (fd, 0);
+         close (fd);
+       }
+    }
+
+  if (__argv[ARG_STDOUT][0] == '-')
+    ; /* Nothing */
+  else if (__argv[ARG_STDOUT][0] == 'z')
+    {
+      fd = open ("NUL:", O_WRONLY);
+      if (fd != 1)
+       {
+         dup2 (fd, 1);
+         close (fd);
+       }
+    }
+  else
+    {
+      fd = atoi (__argv[ARG_STDOUT]);
+      if (fd != 1)
+       {
+         dup2 (fd, 1);
+         close (fd);
+       }
+    }
+
+  if (__argv[ARG_STDERR][0] == '-')
+    ; /* Nothing */
+  else if (__argv[ARG_STDERR][0] == 'z')
+    {
+      fd = open ("NUL:", O_WRONLY);
+      if (fd != 2)
+       {
+         dup2 (fd, 2);
+         close (fd);
+       }
+    }
+  else
+    {
+      fd = atoi (__argv[ARG_STDERR]);
+      if (fd != 2)
+       {
+         dup2 (fd, 2);
+         close (fd);
+       }
+    }
+
+  /* __argv[ARG_WORKING_DIRECTORY] is the directory in which to run the
+   * process.  If "-", don't change directory.
+   */
+  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);
+
+  /* __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
+    mode = P_NOWAIT;
+
+  /* __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.
+   */
+
+  if (debug)
+    {
+      debugstring = g_string_new ("");
+      g_string_append (debugstring,
+                      g_strdup_printf ("calling %s on program %s, __argv: ",
+                                       (__argv[ARG_USE_PATH][0] == 'y' ?
+                                        "spawnvp" : "spawnv"),
+                                       __argv[ARG_PROGRAM]));
+      i = ARG_PROGRAM+1;
+      while (__argv[i])
+       g_string_append (debugstring, __argv[i++]);
+      MessageBox (NULL, debugstring->str, "gspawn-win32-helper", 0);
+    }
+
+  if (__argv[ARG_USE_PATH][0] == 'y')
+    {
+      if (spawnvp (mode, __argv[ARG_PROGRAM], __argv+ARG_PROGRAM) < 0)
+       write_err_and_exit (child_err_report_fd, CHILD_SPAWN_FAILED);
+    }
+  else
+    {
+      if (spawnv (mode, __argv[ARG_PROGRAM], __argv+ARG_PROGRAM) < 0)
+       write_err_and_exit (child_err_report_fd, CHILD_SPAWN_FAILED);
+    }
+
+  return 0;
+}
+
+#endif /* GSPAWN_HELPER */
index 90cddc4..b45b265 100644 (file)
@@ -598,7 +598,7 @@ g_win32_getlocale (void)
                                 */
       switch (sub)
        {
-       case SUBLANG_SERBIAN_LATIN: l = "hr"; break;
+       case SUBLANG_SERBIAN_LATIN: l = "sp"; break;
        case SUBLANG_SERBIAN_CYRILLIC: l = "sr"; break;
        default: l = "hr";      /* ??? */
        }
index 8cc0cbd..918cf7d 100644 (file)
@@ -33,6 +33,7 @@ all : \
        config.h \
        glibconfig.h \
        $(DLLS_TO_BUILD) \
+       gspawn-win32-helper.exe \
        testglib.exe \
        testgdate.exe \
        testgdateparser.exe
@@ -50,6 +51,7 @@ glib_OBJECTS = \
        gdataset.o \
        gdate.o \
        gerror.o \
+       gfileutils.o \
        ghook.o \
        ghash.o \
        giochannel.o \
@@ -62,7 +64,9 @@ glib_OBJECTS = \
        gprimes.o \
        gqueue.o \
        grand.o \
+       gshell.o \
        gslist.o \
+       gspawn-win32.o \
        gthread.o \
        gthreadpool.o \
        gtimer.o \
@@ -92,6 +96,9 @@ makefile.mingw: makefile.mingw.in
 glib-$(GLIB_VER).dll : $(glib_OBJECTS) glib.def
        ./build-dll glib $(GLIB_VER) glib.def $(glib_OBJECTS) $(LIBICONV_LIBS) -luser32 -lwsock32
 
+gspawn-win32-helper.exe : gspawn-win32.c
+       $(CC) $(CFLAGS) -mwindows -DGSPAWN_HELPER  -DG_LOG_DOMAIN=\"gspawn-win32-helper\" -o $@ $< -L . -lglib-$(GLIB_VER)
+
 ################ subdirectories
 
 gmodule/gmodule-$(GLIB_VER).dll : glib-$(GLIB_VER).dll
index 0bd252f..c64cabd 100644 (file)
@@ -30,6 +30,7 @@ all : \
        config.h        \
        glibconfig.h    \
        $(DLLS_TO_BUILD) \
+       gspawn-win32-helper.exe \
        testglib.exe    \
        testgdate.exe   \
        testgdateparser.exe
@@ -46,6 +47,7 @@ glib_OBJECTS = \
        gdataset.obj    \
        gdate.obj       \
        gerror.obj      \
+       gfileutils.obj  \
        gconvert.obj    \
        ghash.obj       \
        ghook.obj       \
@@ -61,9 +63,11 @@ glib_OBJECTS = \
        grand.obj       \
        grel.obj        \
        gscanner.obj    \
+       gshell.obj      \
        gslist.obj      \
        gstrfuncs.obj   \
        gstring.obj     \
+       gspawn-win32.obj \
        gthread.obj     \
        gthreadpool.obj \
        gtimer.obj      \
@@ -89,6 +93,9 @@ makefile.msc: makefile.msc.in
 glib-$(GLIB_VER).dll : $(glib_OBJECTS) glib.def
        $(CC) $(CFLAGS) -LD -Feglib-$(GLIB_VER).dll $(glib_OBJECTS) $(LIBICONV_LIBS) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:glib.def
 
+gspawn-win32-helper.exe : gspawn-win32.c glib-$(GLIB_VER).dll
+       $(CC) $(CFLAGS) -Fe$@ -DG_LOG_DOMAIN=\"gspawn-win32-helper\" gspawn-win32.c glib-$(GLIB_VER).lib $(LDFLAGS) /subsystem:windows
+
 ################ subdirectories
 
 sub-gmodule :
diff --git a/gspawn-win32.c b/gspawn-win32.c
new file mode 100644 (file)
index 0000000..11779da
--- /dev/null
@@ -0,0 +1,1191 @@
+/* gspawn.c - Process launching
+ *
+ *  Copyright 2000 Red Hat, Inc.
+ *
+ * GLib is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * GLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GLib; see the file COPYING.LIB.  If not, write
+ * to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Implementation details on Win32.
+ *
+ * - There is no way to set the no-inherit flag for
+ *   a "file descriptor" in the MS C runtime. The flag is there,
+ *   and the dospawn() function uses it, but unfortunately
+ *   this flag can only be set when opening the file.
+ * - As there is no fork(), we cannot reliably change directory
+ *   before starting the child process. (There might be several threads
+ *   running, and the current directory is common for all threads.)
+ *
+ * Thus, we must in most cases use a helper program to handle closing
+ * of (inherited) file descriptors and changing of directory. In fact,
+ * we do it all the time.
+ *
+ * This source file contains the source for that helper program.
+ * To compile it, #define GSPAWN_HELPER.
+ */
+
+/* Define this to get some logging all the time */
+/* #define G_SPAWN_WIN32_DEBUG */
+
+#include "glib.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <windows.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <io.h>
+#include <process.h>
+
+#ifdef _
+#warning "FIXME remove gettext hack"
+#endif
+
+#define _(x) x
+
+#ifdef G_SPAWN_WIN32_DEBUG
+  static int debug = 1;
+  #define SETUP_DEBUG() /* empty */
+
+#else
+  static int debug = -1;
+  #define SETUP_DEBUG()                                        \
+    G_STMT_START                                       \
+      {                                                        \
+       if (debug == -1)                                \
+         if (getenv ("G_SPAWN_WIN32_DEBUG") != NULL)   \
+           debug = 1;                                  \
+         else                                          \
+           debug = 0;                                  \
+      }                                                        \
+    G_STMT_END
+#endif
+
+enum
+{
+  CHILD_NO_ERROR,
+  CHILD_CHDIR_FAILED,
+  CHILD_SPAWN_FAILED,
+};
+
+enum {
+  ARG_CHILD_ERR_REPORT = 1,
+  ARG_STDIN,
+  ARG_STDOUT,
+  ARG_STDERR,
+  ARG_WORKING_DIRECTORY,
+  ARG_CLOSE_DESCRIPTORS,
+  ARG_USE_PATH,
+  ARG_WAIT,
+  ARG_PROGRAM,
+  ARG_COUNT = ARG_PROGRAM
+};
+
+#ifndef GSPAWN_HELPER
+
+static gboolean make_pipe            (gint                  p[2],
+                                      GError              **error);
+static gboolean fork_exec_with_pipes (gboolean              dont_wait,
+                                     const gchar          *working_directory,
+                                      gchar               **argv,
+                                      gchar               **envp,
+                                      gboolean              close_descriptors,
+                                      gboolean              search_path,
+                                      gboolean              stdout_to_null,
+                                      gboolean              stderr_to_null,
+                                      gboolean              child_inherits_stdin,
+                                      GSpawnChildSetupFunc  child_setup,
+                                      gpointer              user_data,
+                                      gint                 *standard_input,
+                                      gint                 *standard_output,
+                                      gint                 *standard_error,
+                                     gint                 *exit_status,
+                                      GError              **error);
+
+GQuark
+g_spawn_error_quark (void)
+{
+  static GQuark quark = 0;
+  if (quark == 0)
+    quark = g_quark_from_static_string ("g-exec-error-quark");
+  return quark;
+}
+
+/**
+ * g_spawn_async:
+ * @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
+ * @flags: flags from #GSpawnFlags
+ * @child_setup: function to run in the child just before exec()
+ * @user_data: user data for @child_setup
+ * @child_pid: return location for child process ID, or NULL
+ * @error: return location for error
+ * 
+ * See g_spawn_async_with_pipes() for a full description; this function
+ * simply calls the g_spawn_async_with_pipes() without any pipes.
+ * 
+ * Return value: TRUE on success, FALSE if error is set
+ **/
+gboolean
+g_spawn_async (const gchar          *working_directory,
+               gchar               **argv,
+               gchar               **envp,
+               GSpawnFlags           flags,
+               GSpawnChildSetupFunc  child_setup,
+               gpointer              user_data,
+               gint                 *child_pid,
+               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_pid,
+                                   NULL, NULL, NULL,
+                                   error);
+}
+
+/* Avoids a danger in threaded situations (calling close()
+ * on a file descriptor twice, and another thread has
+ * re-opened it since the first close)
+ */
+static gint
+close_and_invalidate (gint *fd)
+{
+  gint ret;
+
+  ret = close (*fd);
+  *fd = -1;
+
+  return ret;
+}
+
+typedef enum
+{
+  READ_FAILED = 0, /* FALSE */
+  READ_OK,
+  READ_EOF
+} ReadResult;
+
+static ReadResult
+read_data (GString     *str,
+           GIOChannel  *iochannel,
+           GError     **error)
+{
+  GIOError gioerror;
+  gint bytes;
+  gchar buf[4096];
+
+ again:
+  
+  gioerror = g_io_channel_read (iochannel, buf, sizeof (buf), &bytes);
+
+  if (bytes == 0)
+    return READ_EOF;
+  else if (bytes > 0)
+    {
+      g_string_append_len (str, buf, bytes);
+      return READ_OK;
+    }
+  else if (gioerror == G_IO_ERROR_AGAIN)
+    goto again;
+  else if (gioerror != G_IO_ERROR_NONE)
+    {
+      g_set_error (error,
+                   G_SPAWN_ERROR,
+                   G_SPAWN_ERROR_READ,
+                   _("Failed to read data from child process"));
+      
+      return READ_FAILED;
+    }
+  else
+    return READ_OK;
+}
+
+/**
+ * g_spawn_sync:
+ * @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
+ * @flags: flags from #GSpawnFlags
+ * @child_setup: function to run in the child just before exec()
+ * @user_data: user data for @child_setup
+ * @standard_output: return location for child output 
+ * @standard_error: return location for child error messages
+ * @exit_status: child exit status, as returned by waitpid()
+ * @error: return location for error
+ *
+ * Executes a child synchronously (waits for the child to exit before returning).
+ * All output from the child is stored in @standard_output and @standard_error,
+ * if those parameters are non-NULL. If @exit_status is non-NULL, the exit status
+ * of the child is stored there as it would be by waitpid(); standard UNIX
+ * macros such as WIFEXITED() and WEXITSTATUS() 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.
+ * 
+ * Return value: TRUE on success, FALSE if an error was set.
+ **/
+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)     
+{
+  gint outpipe = -1;
+  gint errpipe = -1;
+  GIOChannel *outchannel = NULL;
+  GIOChannel *errchannel = NULL;
+  GPollFD outfd, errfd;
+  GPollFD fds[2];
+  gint nfds;
+  gint outindex = -1;
+  gint errindex = -1;
+  gint ret;
+  GString *outstr = NULL;
+  GString *errstr = NULL;
+  gboolean failed;
+  gint status;
+  
+  g_return_val_if_fail (argv != NULL, FALSE);
+  g_return_val_if_fail (!(flags & G_SPAWN_DO_NOT_REAP_CHILD), FALSE);
+  g_return_val_if_fail (standard_output == NULL ||
+                        !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
+  g_return_val_if_fail (standard_error == NULL ||
+                        !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
+  
+  /* Just to ensure segfaults if callers try to use
+   * these when an error is reported.
+   */
+  if (standard_output)
+    *standard_output = NULL;
+
+  if (standard_error)
+    *standard_error = NULL;
+  
+  if (!fork_exec_with_pipes (FALSE,
+                             working_directory,
+                             argv,
+                             envp,
+                             !(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN),
+                             (flags & G_SPAWN_SEARCH_PATH) != 0,
+                             (flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0,
+                             (flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0,
+                             (flags & G_SPAWN_CHILD_INHERITS_STDIN) != 0,
+                             child_setup,
+                             user_data,
+                             NULL,
+                             standard_output ? &outpipe : NULL,
+                             standard_error ? &errpipe : NULL,
+                            &status,
+                             error))
+    return FALSE;
+
+  /* Read data from child. */
+  
+  failed = FALSE;
+
+  if (outpipe >= 0)
+    {
+      outstr = g_string_new ("");
+      outchannel = g_io_channel_win32_new_fd (outpipe);
+      g_io_channel_win32_make_pollfd (outchannel,
+                                     G_IO_IN | G_IO_ERR | G_IO_HUP,
+                                     &outfd);
+    }
+      
+  if (errpipe >= 0)
+    {
+      errstr = g_string_new ("");
+      errchannel = g_io_channel_win32_new_fd (errpipe);
+      g_io_channel_win32_make_pollfd (errchannel,
+                                     G_IO_IN | G_IO_ERR | G_IO_HUP,
+                                     &errfd);
+    }
+
+  /* Read data until we get EOF on both pipes. */
+  while (!failed &&
+         (outpipe >= 0 ||
+          errpipe >= 0))
+    {
+      nfds = 0;
+      if (outpipe >= 0)
+       {
+         fds[nfds] = outfd;
+         outindex = nfds;
+         nfds++;
+       }
+      if (errpipe >= 0)
+       {
+         fds[nfds] = errfd;
+         errindex = nfds;
+         nfds++;
+       }
+
+      if (debug)
+       g_print ("%s:g_spawn_sync: calling g_io_channel_win32_poll, nfds=%d\n",
+                __FILE__, nfds);
+
+      ret = g_io_channel_win32_poll (fds, nfds, -1);
+
+      if (ret < 0)
+        {
+          failed = TRUE;
+
+          g_set_error (error,
+                       G_SPAWN_ERROR,
+                       G_SPAWN_ERROR_READ,
+                       _("Unexpected error in g_io_channel_win32_poll() reading data from a child process"));
+              
+          break;
+        }
+
+      if (outpipe >= 0 && (fds[outindex].revents & G_IO_IN))
+        {
+          switch (read_data (outstr, outchannel, error))
+            {
+            case READ_FAILED:
+             if (debug)
+               g_print ("g_spawn_sync: outchannel: READ_FAILED\n");
+              failed = TRUE;
+              break;
+            case READ_EOF:
+             if (debug)
+               g_print ("g_spawn_sync: outchannel: READ_EOF\n");
+              g_io_channel_unref (outchannel);
+             outchannel = NULL;
+              close_and_invalidate (&outpipe);
+              break;
+            default:
+             if (debug)
+               g_print ("g_spawn_sync: outchannel: OK\n");
+              break;
+            }
+
+          if (failed)
+            break;
+        }
+
+      if (errpipe >= 0 && (fds[errindex].revents & G_IO_IN))
+        {
+          switch (read_data (errstr, errchannel, error))
+            {
+            case READ_FAILED:
+             if (debug)
+               g_print ("g_spawn_sync: errchannel: READ_FAILED\n");
+              failed = TRUE;
+              break;
+            case READ_EOF:
+             if (debug)
+               g_print ("g_spawn_sync: errchannel: READ_EOF\n");
+             g_io_channel_unref (errchannel);
+             errchannel = NULL;
+              close_and_invalidate (&errpipe);
+              break;
+            default:
+             if (debug)
+               g_print ("g_spawn_sync: errchannel: OK\n");
+              break;
+            }
+
+          if (failed)
+            break;
+        }
+    }
+
+  /* These should only be open still if we had an error.  */
+  
+  if (outchannel != NULL)
+    g_io_channel_unref (outchannel);
+  if (errchannel != NULL)
+    g_io_channel_unref (errchannel);
+  if (outpipe >= 0)
+    close_and_invalidate (&outpipe);
+  if (errpipe >= 0)
+    close_and_invalidate (&errpipe);
+  
+  if (failed)
+    {
+      if (outstr)
+        g_string_free (outstr, TRUE);
+      if (errstr)
+        g_string_free (errstr, TRUE);
+
+      return FALSE;
+    }
+  else
+    {
+      if (exit_status)
+        *exit_status = status;
+      
+      if (standard_output)        
+        *standard_output = g_string_free (outstr, FALSE);
+
+      if (standard_error)
+        *standard_error = g_string_free (errstr, FALSE);
+
+      return TRUE;
+    }
+}
+
+/**
+ * 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
+ * @flags: flags from #GSpawnFlags
+ * @child_setup: function to run in the child just before exec()
+ * @user_data: user data for @child_setup
+ * @child_pid: return location for child process ID, or NULL
+ * @standard_input: return location for file descriptor to write to child's stdin, or NULL
+ * @standard_output: return location for file descriptor to read child's stdout, or NULL
+ * @standard_error: return location for file descriptor to read child's stderr, or NULL
+ * @error: return location for error
+ *
+ * Executes a child program asynchronously (your program will not
+ * block waiting for the child to exit). The child program is
+ * specified by the only argument that must be provided, @argv. @argv
+ * should be a NULL-terminated array of strings, to be passed as the
+ * argument vector for the child. The first string in @argv is of
+ * course the name of the program to execute. By default, the name of
+ * the program must be a full path; the PATH shell variable will only
+ * be searched if you pass the %G_SPAWN_SEARCH_PATH flag.
+ *
+ * @envp is a NULL-terminated array of strings, where each string
+ * has the form <literal>KEY=VALUE</literal>. This will become
+ * the child's environment. If @envp is NULL, the child inherits its
+ * parent's environment.
+ *
+ * @flags should be the bitwise OR of any flags you want to affect the
+ * function's behavior. The %G_SPAWN_DO_NOT_REAP_CHILD means that the
+ * child will not be automatically reaped; you must call waitpid() or
+ * handle SIGCHLD yourself, or the child will become a zombie.
+ * %G_SPAWN_LEAVE_DESCRIPTORS_OPEN means that the parent's open file
+ * descriptors will be inherited by the child; otherwise all
+ * descriptors except stdin/stdout/stderr will be closed before
+ * calling exec() in the child. %G_SPAWN_SEARCH_PATH means that
+ * <literal>argv[0]</literal> need not be an absolute path, it
+ * will be looked for in the user's PATH. %G_SPAWN_STDOUT_TO_DEV_NULL
+ * means that the child's standad output will be discarded, instead
+ * of going to the same location as the parent's standard output.
+ * %G_SPAWN_STDERR_TO_DEV_NULL means that the child's standard error
+ * will be discarded. %G_SPAWN_CHILD_INHERITS_STDIN means that
+ * the child will inherit the parent's standard input (by default,
+ * the child's standard input is attached to /dev/null).
+ *
+ * @child_setup and @user_data are a function and user data to be
+ * called in the child after GLib has performed all the setup it plans
+ * to perform (including creating pipes, closing file descriptors,
+ * etc.) but before calling exec(). That is, @child_setup is called
+ * just before calling exec() in the child. Obviously actions taken in
+ * this function will only affect the child, not the parent. 
+ *
+ * If non-NULL, @child_pid will be filled with the child's process
+ * ID. You can use the process ID to send signals to the child, or
+ * to waitpid() if you specified the %G_SPAWN_DO_NOT_REAP_CHILD flag.
+ *
+ * If non-NULL, the @standard_input, @standard_output, @standard_error
+ * locations will be filled with file descriptors for writing to the child's
+ * standard input or reading from its standard output or standard error.
+ * The caller of g_spawn_async_with_pipes() must close these file descriptors
+ * when they are no longer in use. If these parameters are NULL, the
+ * corresponding pipe won't be created.
+ *
+ * @error can be NULL to ignore errors, or non-NULL to report errors.
+ * If an error is set, the function returns FALSE. Errors
+ * are reported even if they occur in the child (for example if the
+ * executable in <literal>argv[0]</literal> is not found). Typically
+ * the <literal>message</literal> field of returned errors should be displayed
+ * to users. Possible errors are those from the #G_SPAWN_ERROR domain.
+ *
+ * If an error occurs, @child_pid, @standard_input, @standard_output,
+ * and @standard_error will not be filled with valid values.
+ * 
+ * Return value: TRUE on success, FALSE if an error was set
+ **/
+gboolean
+g_spawn_async_with_pipes (const gchar          *working_directory,
+                          gchar               **argv,
+                          gchar               **envp,
+                          GSpawnFlags           flags,
+                          GSpawnChildSetupFunc  child_setup,
+                          gpointer              user_data,
+                          gint                 *child_pid,
+                          gint                 *standard_input,
+                          gint                 *standard_output,
+                          gint                 *standard_error,
+                          GError              **error)
+{
+  g_return_val_if_fail (argv != NULL, FALSE);
+  g_return_val_if_fail (standard_output == NULL ||
+                        !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
+  g_return_val_if_fail (standard_error == NULL ||
+                        !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
+  /* can't inherit stdin if we have an input pipe. */
+  g_return_val_if_fail (standard_input == NULL ||
+                        !(flags & G_SPAWN_CHILD_INHERITS_STDIN), FALSE);
+  
+  return fork_exec_with_pipes (!(flags & G_SPAWN_DO_NOT_REAP_CHILD),
+                               working_directory,
+                               argv,
+                               envp,
+                               !(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN),
+                               (flags & G_SPAWN_SEARCH_PATH) != 0,
+                               (flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0,
+                               (flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0,
+                               (flags & G_SPAWN_CHILD_INHERITS_STDIN) != 0,
+                               child_setup,
+                               user_data,
+                               standard_input,
+                               standard_output,
+                               standard_error,
+                              NULL,
+                               error);
+}
+
+/**
+ * g_spawn_command_line_sync:
+ * @command_line: a command line 
+ * @standard_output: return location for child output
+ * @standard_error: return location for child errors
+ * @exit_status: return location for child exit status
+ * @error: return location for errors
+ *
+ * A simple version of g_spawn_sync() with little-used parameters
+ * removed, taking a command line instead of an argument vector.  See
+ * g_spawn_sync() for full details. @command_line will be parsed by
+ * g_shell_parse_argv(). Unlike g_spawn_sync(), the %G_SPAWN_SEARCH_PATH flag
+ * is enabled. Note that %G_SPAWN_SEARCH_PATH can have security
+ * implications, so consider using g_spawn_sync() directly if
+ * appropriate. Possible errors are those from g_spawn_sync() and those
+ * from g_shell_parse_argv().
+ * 
+ * Return value: TRUE on success, FALSE if an error was set
+ **/
+gboolean
+g_spawn_command_line_sync (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 (NULL,
+                         argv,
+                         NULL,
+                         G_SPAWN_SEARCH_PATH,
+                         NULL,
+                         NULL,
+                         standard_output,
+                         standard_error,
+                         exit_status,
+                         error);
+  g_strfreev (argv);
+
+  return retval;
+}
+
+/**
+ * g_spawn_command_line_async:
+ * @command_line: a command line
+ * @error: return location for errors
+ * 
+ * A simple version of g_spawn_async() that parses a command line with
+ * g_shell_parse_argv() and passes it to g_spawn_async(). Runs a
+ * command line in the background. Unlike g_spawn_async(), the
+ * %G_SPAWN_SEARCH_PATH flag is enabled, other flags are not. Note
+ * that %G_SPAWN_SEARCH_PATH can have security implications, so
+ * consider using g_spawn_async() directly if appropriate. Possible
+ * errors are those from g_shell_parse_argv() and g_spawn_async().
+ * 
+ * Return value: TRUE on success, FALSE if error is set.
+ **/
+gboolean
+g_spawn_command_line_async (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 (NULL,
+                          argv,
+                          NULL,
+                          G_SPAWN_SEARCH_PATH,
+                          NULL,
+                          NULL,
+                          NULL,
+                          error);
+  g_strfreev (argv);
+
+  return retval;
+}
+
+static gint
+do_exec (gboolean              dont_wait,
+        gint                  child_err_report_fd,
+         gint                  stdin_fd,
+         gint                  stdout_fd,
+         gint                  stderr_fd,
+         const gchar          *working_directory,
+         gchar               **argv,
+         gchar               **envp,
+         gboolean              close_descriptors,
+         gboolean              search_path,
+         gboolean              stdout_to_null,
+         gboolean              stderr_to_null,
+         gboolean              child_inherits_stdin,
+         GSpawnChildSetupFunc  child_setup,
+         gpointer              user_data)
+{
+  gchar **new_argv;
+  gchar args[ARG_COUNT][10];
+  gint i;
+  int argc = 0;
+
+  SETUP_DEBUG();
+
+  while (argv[argc])
+    ++argc;
+
+  new_argv = g_new (gchar *, argc + 1 + ARG_COUNT);
+
+  new_argv[0] = "gspawn-win32-helper";
+  sprintf (args[ARG_CHILD_ERR_REPORT], "%d", child_err_report_fd);
+  new_argv[ARG_CHILD_ERR_REPORT] = args[ARG_CHILD_ERR_REPORT];
+
+  if (stdin_fd >= 0)
+    {
+      sprintf (args[ARG_STDIN], "%d", stdin_fd);
+      new_argv[ARG_STDIN] = args[ARG_STDIN];
+    }
+  else if (child_inherits_stdin)
+    {
+      /* Let stdin be alone */
+      new_argv[ARG_STDIN] = "-";
+    }
+  else
+    {
+      /* Keep process from blocking on a read of stdin */
+      new_argv[ARG_STDIN] = "z";
+    }
+
+  if (stdout_fd >= 0)
+    {
+      sprintf (args[ARG_STDOUT], "%d", stdout_fd);
+      new_argv[ARG_STDOUT] = args[ARG_STDOUT];
+    }
+  else if (stdout_to_null)
+    {
+      new_argv[ARG_STDOUT] = "z";
+    }
+  else
+    {
+      new_argv[ARG_STDOUT] = "-";
+    }
+
+  if (stderr_fd >= 0)
+    {
+      sprintf (args[ARG_STDERR], "%d", stderr_fd);
+      new_argv[ARG_STDERR] = args[ARG_STDERR];
+    }
+  else if (stderr_to_null)
+    {
+      new_argv[ARG_STDERR] = "z";
+    }
+  else
+    {
+      new_argv[ARG_STDERR] = "-";
+    }
+
+  if (working_directory && *working_directory)
+    new_argv[ARG_WORKING_DIRECTORY] = working_directory;
+  else
+    new_argv[ARG_WORKING_DIRECTORY] = "-";
+
+  if (close_descriptors)
+    new_argv[ARG_CLOSE_DESCRIPTORS] = "y";
+  else
+    new_argv[ARG_CLOSE_DESCRIPTORS] = "-";
+
+  if (search_path)
+    new_argv[ARG_USE_PATH] = "y";
+  else
+    new_argv[ARG_USE_PATH] = "-";
+
+  if (dont_wait)
+    new_argv[ARG_WAIT] = "-";
+  else
+    new_argv[ARG_WAIT] = "w";
+
+  for (i = 0; i <= argc; i++)
+    new_argv[ARG_PROGRAM + i] = argv[i];
+
+  /* Call user function just before we execute the helper program,
+   * which executes the program. Dunno what's the usefulness of this.
+   * A child setup function used on Unix probably isn't of much use
+   * as such on Win32, anyhow.
+   */
+  if (child_setup)
+    {
+      (* child_setup) (user_data);
+    }
+
+  if (debug)
+    {
+      g_print ("calling gspawn-win32-helper with argv:\n");
+      for (i = 0; i < argc + 1 + ARG_COUNT; i++)
+       g_print ("argv[%d]: %s\n", i, (new_argv[i] ? new_argv[i] : "NULL"));
+    }
+  
+  if (envp != NULL)
+    /* Let's hope envp hasn't mucked with PATH so that
+     * gspawn-win32-helper.exe isn't found.
+     */
+    spawnvpe (P_NOWAIT, "gspawn-win32-helper", new_argv, envp);
+  else
+    spawnvp (P_NOWAIT, "gspawn-win32-helper", new_argv);
+
+  /* FIXME: What if gspawn-win32-helper.exe isn't found? */
+
+  /* Close the child_err_report_fd and the other process's ends of the
+   * pipes in this process, otherwise the reader will never get
+   * EOF.
+   */
+  close (child_err_report_fd);
+  if (stdin_fd >= 0)
+    close (stdin_fd);
+  if (stdout_fd >= 0)
+    close (stdout_fd);
+  if (stderr_fd >= 0)
+    close (stderr_fd);
+
+  g_free (new_argv);
+
+  return 0;
+}
+
+static gboolean
+read_ints (int      fd,
+           gint*    buf,
+           gint     n_ints_in_buf,
+           gint    *n_ints_read,
+           GError **error)
+{
+  gint bytes = 0;
+  
+  while (bytes < sizeof(gint)*n_ints_in_buf)
+    {
+      gint chunk;
+
+      if (debug)
+       g_print ("%s:read_ints: trying to read %d bytes from pipe...\n",
+                __FILE__,
+                sizeof(gint)*n_ints_in_buf - bytes);
+
+      chunk = read (fd, ((gchar*)buf) + bytes,
+                   sizeof(gint)*n_ints_in_buf - bytes);
+
+      if (debug)
+       g_print ("... got %d bytes\n", chunk);
+          
+      if (chunk < 0)
+        {
+          /* Some weird shit happened, bail out */
+              
+          g_set_error (error,
+                       G_SPAWN_ERROR,
+                       G_SPAWN_ERROR_FAILED,
+                       _("Failed to read from child pipe (%s)"),
+                       g_strerror (errno));
+
+          return FALSE;
+        }
+      else if (chunk == 0)
+        break; /* EOF */
+      else
+       bytes += chunk;
+    }
+
+  *n_ints_read = bytes/sizeof(gint);
+
+  return TRUE;
+}
+
+static gboolean
+fork_exec_with_pipes (gboolean              dont_wait,
+                      const gchar          *working_directory,
+                      gchar               **argv,
+                      gchar               **envp,
+                      gboolean              close_descriptors,
+                      gboolean              search_path,
+                      gboolean              stdout_to_null,
+                      gboolean              stderr_to_null,
+                     gboolean              child_inherits_stdin,
+                      GSpawnChildSetupFunc  child_setup,
+                      gpointer              user_data,
+                      gint                 *standard_input,
+                      gint                 *standard_output,
+                      gint                 *standard_error,
+                     gint                 *exit_status,
+                      GError              **error)     
+{
+  gint stdin_pipe[2] = { -1, -1 };
+  gint stdout_pipe[2] = { -1, -1 };
+  gint stderr_pipe[2] = { -1, -1 };
+  gint child_err_report_pipe[2] = { -1, -1 };
+  gint status;
+  gint bytes;
+  gint buf[2];
+  gint n_ints = 0;
+  
+  if (!make_pipe (child_err_report_pipe, error))
+    return FALSE;
+
+  if (standard_input && !make_pipe (stdin_pipe, error))
+    goto cleanup_and_fail;
+  
+  if (standard_output && !make_pipe (stdout_pipe, error))
+    goto cleanup_and_fail;
+
+  if (standard_error && !make_pipe (stderr_pipe, error))
+    goto cleanup_and_fail;
+
+  status = do_exec (dont_wait,
+                   child_err_report_pipe[1],
+                   stdin_pipe[0],
+                   stdout_pipe[1],
+                   stderr_pipe[1],
+                   working_directory,
+                   argv,
+                   envp,
+                   close_descriptors,
+                   search_path,
+                   stdout_to_null,
+                   stderr_to_null,
+                   child_inherits_stdin,
+                   child_setup,
+                   user_data);
+      
+  if (!read_ints (child_err_report_pipe[0],
+                 buf, 2, &n_ints,
+                 error))
+    goto cleanup_and_fail;
+        
+  if (n_ints == 2)
+    {
+      /* Error from the child. */
+      
+      switch (buf[0])
+       {
+       case CHILD_NO_ERROR:
+         break;
+         
+       case CHILD_CHDIR_FAILED:
+         g_set_error (error,
+                      G_SPAWN_ERROR,
+                      G_SPAWN_ERROR_CHDIR,
+                      _("Failed to change to directory '%s' (%s)"),
+                      working_directory,
+                      g_strerror (buf[1]));
+         goto cleanup_and_fail;
+         
+       case CHILD_SPAWN_FAILED:
+         g_set_error (error,
+                      G_SPAWN_ERROR,
+                      G_SPAWN_ERROR_FAILED,
+                      _("Failed to execute child process (%s)"),
+                      g_strerror (buf[1]));
+         goto cleanup_and_fail;
+       }
+    }
+
+  /* Success against all odds! return the information */
+      
+  if (standard_input)
+    *standard_input = stdin_pipe[1];
+  if (standard_output)
+    *standard_output = stdout_pipe[0];
+  if (standard_error)
+    *standard_error = stderr_pipe[0];
+  if (exit_status)
+    *exit_status = status;
+  
+  return TRUE;
+
+ cleanup_and_fail:
+  close_and_invalidate (&child_err_report_pipe[0]);
+  close_and_invalidate (&child_err_report_pipe[1]);
+  close_and_invalidate (&stdin_pipe[0]);
+  close_and_invalidate (&stdin_pipe[1]);
+  close_and_invalidate (&stdout_pipe[0]);
+  close_and_invalidate (&stdout_pipe[1]);
+  close_and_invalidate (&stderr_pipe[0]);
+  close_and_invalidate (&stderr_pipe[1]);
+
+  return FALSE;
+}
+
+static gboolean
+make_pipe (gint     p[2],
+           GError **error)
+{
+  if (pipe (p) < 0)
+    {
+      g_set_error (error,
+                   G_SPAWN_ERROR,
+                   G_SPAWN_ERROR_FAILED,
+                   _("Failed to create pipe for communicating with child process (%s)"),
+                   g_strerror (errno));
+      return FALSE;
+    }
+  else
+    return TRUE;
+}
+
+#else /* GSPAWN_HELPER */
+
+static void
+write_err_and_exit (gint fd,
+                   gint msg)
+{
+  gint en = errno;
+  
+  write (fd, &msg, sizeof(msg));
+  write (fd, &en, sizeof(en));
+  
+  _exit (1);
+}
+
+static void
+write_no_error (gint fd)
+{
+  gint msg = CHILD_NO_ERROR;
+  gint en = 0;
+
+  write (fd, &msg, sizeof(msg));
+  write (fd, &en, sizeof(en));
+}
+
+#ifdef __GNUC__
+#  ifndef _stdcall
+#    define _stdcall  __attribute__((stdcall))
+#  endif
+#endif
+
+/* We build gspawn-win32-helper.exe as a Windows GUI application
+ * to avoid any temporarily flashing console windows in case
+ * the gspawn function is invoked by a GUI program. Thus, no main()
+ * but a WinMain(). We do, however, still use argc and argv tucked
+ * away in the global __argc and __argv by the C runtime startup code.
+ */
+
+int _stdcall
+WinMain (struct HINSTANCE__ *hInstance,
+        struct HINSTANCE__ *hPrevInstance,
+        char               *lpszCmdLine,
+        int                 nCmdShow)
+{
+  int child_err_report_fd;
+  int i;
+  int fd;
+  int mode;
+  GString *debugstring;
+
+  SETUP_DEBUG();
+
+  if (debug)
+    {
+      debugstring = g_string_new ("");
+
+      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);
+
+  /* argv[ARG_CHILD_ERR_REPORT] is the file descriptor onto which
+   * write error messages.
+   */
+  child_err_report_fd = atoi (__argv[ARG_CHILD_ERR_REPORT]);
+
+  /* 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:.
+   */
+  if (__argv[ARG_STDIN][0] == '-')
+    ; /* Nothing */
+  else if (__argv[ARG_STDIN][0] == 'z')
+    {
+      fd = open ("NUL:", O_RDONLY);
+      if (fd != 0)
+       {
+         dup2 (fd, 0);
+         close (fd);
+       }
+    }
+  else
+    {
+      fd = atoi (__argv[ARG_STDIN]);
+      if (fd != 0)
+       {
+         dup2 (fd, 0);
+         close (fd);
+       }
+    }
+
+  if (__argv[ARG_STDOUT][0] == '-')
+    ; /* Nothing */
+  else if (__argv[ARG_STDOUT][0] == 'z')
+    {
+      fd = open ("NUL:", O_WRONLY);
+      if (fd != 1)
+       {
+         dup2 (fd, 1);
+         close (fd);
+       }
+    }
+  else
+    {
+      fd = atoi (__argv[ARG_STDOUT]);
+      if (fd != 1)
+       {
+         dup2 (fd, 1);
+         close (fd);
+       }
+    }
+
+  if (__argv[ARG_STDERR][0] == '-')
+    ; /* Nothing */
+  else if (__argv[ARG_STDERR][0] == 'z')
+    {
+      fd = open ("NUL:", O_WRONLY);
+      if (fd != 2)
+       {
+         dup2 (fd, 2);
+         close (fd);
+       }
+    }
+  else
+    {
+      fd = atoi (__argv[ARG_STDERR]);
+      if (fd != 2)
+       {
+         dup2 (fd, 2);
+         close (fd);
+       }
+    }
+
+  /* __argv[ARG_WORKING_DIRECTORY] is the directory in which to run the
+   * process.  If "-", don't change directory.
+   */
+  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);
+
+  /* __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
+    mode = P_NOWAIT;
+
+  /* __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.
+   */
+
+  if (debug)
+    {
+      debugstring = g_string_new ("");
+      g_string_append (debugstring,
+                      g_strdup_printf ("calling %s on program %s, __argv: ",
+                                       (__argv[ARG_USE_PATH][0] == 'y' ?
+                                        "spawnvp" : "spawnv"),
+                                       __argv[ARG_PROGRAM]));
+      i = ARG_PROGRAM+1;
+      while (__argv[i])
+       g_string_append (debugstring, __argv[i++]);
+      MessageBox (NULL, debugstring->str, "gspawn-win32-helper", 0);
+    }
+
+  if (__argv[ARG_USE_PATH][0] == 'y')
+    {
+      if (spawnvp (mode, __argv[ARG_PROGRAM], __argv+ARG_PROGRAM) < 0)
+       write_err_and_exit (child_err_report_fd, CHILD_SPAWN_FAILED);
+    }
+  else
+    {
+      if (spawnv (mode, __argv[ARG_PROGRAM], __argv+ARG_PROGRAM) < 0)
+       write_err_and_exit (child_err_report_fd, CHILD_SPAWN_FAILED);
+    }
+
+  return 0;
+}
+
+#endif /* GSPAWN_HELPER */
index 90cddc4..b45b265 100644 (file)
--- a/gwin32.c
+++ b/gwin32.c
@@ -598,7 +598,7 @@ g_win32_getlocale (void)
                                 */
       switch (sub)
        {
-       case SUBLANG_SERBIAN_LATIN: l = "hr"; break;
+       case SUBLANG_SERBIAN_LATIN: l = "sp"; break;
        case SUBLANG_SERBIAN_CYRILLIC: l = "sr"; break;
        default: l = "hr";      /* ??? */
        }
index 8cc0cbd..918cf7d 100644 (file)
@@ -33,6 +33,7 @@ all : \
        config.h \
        glibconfig.h \
        $(DLLS_TO_BUILD) \
+       gspawn-win32-helper.exe \
        testglib.exe \
        testgdate.exe \
        testgdateparser.exe
@@ -50,6 +51,7 @@ glib_OBJECTS = \
        gdataset.o \
        gdate.o \
        gerror.o \
+       gfileutils.o \
        ghook.o \
        ghash.o \
        giochannel.o \
@@ -62,7 +64,9 @@ glib_OBJECTS = \
        gprimes.o \
        gqueue.o \
        grand.o \
+       gshell.o \
        gslist.o \
+       gspawn-win32.o \
        gthread.o \
        gthreadpool.o \
        gtimer.o \
@@ -92,6 +96,9 @@ makefile.mingw: makefile.mingw.in
 glib-$(GLIB_VER).dll : $(glib_OBJECTS) glib.def
        ./build-dll glib $(GLIB_VER) glib.def $(glib_OBJECTS) $(LIBICONV_LIBS) -luser32 -lwsock32
 
+gspawn-win32-helper.exe : gspawn-win32.c
+       $(CC) $(CFLAGS) -mwindows -DGSPAWN_HELPER  -DG_LOG_DOMAIN=\"gspawn-win32-helper\" -o $@ $< -L . -lglib-$(GLIB_VER)
+
 ################ subdirectories
 
 gmodule/gmodule-$(GLIB_VER).dll : glib-$(GLIB_VER).dll
index 0bd252f..c64cabd 100644 (file)
@@ -30,6 +30,7 @@ all : \
        config.h        \
        glibconfig.h    \
        $(DLLS_TO_BUILD) \
+       gspawn-win32-helper.exe \
        testglib.exe    \
        testgdate.exe   \
        testgdateparser.exe
@@ -46,6 +47,7 @@ glib_OBJECTS = \
        gdataset.obj    \
        gdate.obj       \
        gerror.obj      \
+       gfileutils.obj  \
        gconvert.obj    \
        ghash.obj       \
        ghook.obj       \
@@ -61,9 +63,11 @@ glib_OBJECTS = \
        grand.obj       \
        grel.obj        \
        gscanner.obj    \
+       gshell.obj      \
        gslist.obj      \
        gstrfuncs.obj   \
        gstring.obj     \
+       gspawn-win32.obj \
        gthread.obj     \
        gthreadpool.obj \
        gtimer.obj      \
@@ -89,6 +93,9 @@ makefile.msc: makefile.msc.in
 glib-$(GLIB_VER).dll : $(glib_OBJECTS) glib.def
        $(CC) $(CFLAGS) -LD -Feglib-$(GLIB_VER).dll $(glib_OBJECTS) $(LIBICONV_LIBS) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:glib.def
 
+gspawn-win32-helper.exe : gspawn-win32.c glib-$(GLIB_VER).dll
+       $(CC) $(CFLAGS) -Fe$@ -DG_LOG_DOMAIN=\"gspawn-win32-helper\" gspawn-win32.c glib-$(GLIB_VER).lib $(LDFLAGS) /subsystem:windows
+
 ################ subdirectories
 
 sub-gmodule :
index b010455..42deca5 100644 (file)
@@ -25,7 +25,9 @@ TESTS = \
        queue-test.exe  \
        rand-test.exe   \
        relation-test.exe\
+       shell-test.exe  \
        slist-test.exe  \
+       spawn-test.exe  \
        strfunc-test.exe\
        string-test.exe \
        thread-test.exe \
index 445df1b..7029215 100644 (file)
@@ -26,7 +26,9 @@ TESTS = \
        queue-test.exe  \
        rand-test.exe   \
        relation-test.exe\
+       shell-test.exe  \
        slist-test.exe  \
+       spawn-test.exe  \
 # strfunc-test doesn't compile with MSVC
 #      strfunc-test.exe\
        string-test.exe \
index b4091fa..87ad8ee 100644 (file)
@@ -60,6 +60,7 @@ run_tests (void)
   printf ("Errors after this are not supposed to happen:\n");
   
   err = NULL;
+#ifdef G_OS_UNIX
   if (!g_spawn_command_line_sync ("/bin/sh -c 'echo hello'",
                                   &output, NULL, NULL,
                                   &err))
@@ -82,6 +83,33 @@ run_tests (void)
 
       g_free (output);
     }
+#else
+#ifdef G_OS_WIN32
+  if (!g_spawn_command_line_sync ("ipconfig /all",
+                                  &output, NULL, NULL,
+                                  &err))
+    {
+      fprintf (stderr, "Error: %s\n", err->message);
+      g_error_free (err);
+      exit (1);
+    }
+  else
+    {
+      g_assert (output != NULL);
+      
+      if (strstr (output, "IP Configuration") == 0)
+        {
+          printf ("output was '%s', should have contained 'IP Configuration'\n",
+                  output);
+
+          exit (1);
+        }
+
+      g_free (output);
+    }
+
+#endif
+#endif
 }
 
 int