+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.
+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.
+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.
+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.
+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.
+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.
+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.
+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.
*/
#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__ */
* 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>
#include <sys/stat.h>
#include <fcntl.h>
+#ifndef S_ISLNK
+# define S_ISLNK(x) 0
+#endif
#define _(x) x
return TRUE;
}
+#ifndef G_OS_WIN32
+
static gboolean
get_contents_regfile (const gchar *filename,
struct stat *stat_buf,
}
}
-#ifdef G_OS_WIN32
+#else /* G_OS_WIN32 */
+
static gboolean
get_contents_win32 (const gchar *filename,
gchar **contents,
return get_contents_stdio (filename, f, contents, length, error);
}
+
#endif
/**
return get_contents_posix (filename, contents, length, error);
#endif
}
-
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);
gint n_fds,
gint timeout)
{
- int i;
int result;
- gboolean debug = FALSE;
g_return_val_if_fail (n_fds >= 0, 0);
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
g_error_matches
g_error_new
g_error_new_literal
+ g_file_test
g_filename_from_utf8
g_filename_to_utf8
g_free
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
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
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
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
*/
#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__ */
* 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>
#include <sys/stat.h>
#include <fcntl.h>
+#ifndef S_ISLNK
+# define S_ISLNK(x) 0
+#endif
#define _(x) x
return TRUE;
}
+#ifndef G_OS_WIN32
+
static gboolean
get_contents_regfile (const gchar *filename,
struct stat *stat_buf,
}
}
-#ifdef G_OS_WIN32
+#else /* G_OS_WIN32 */
+
static gboolean
get_contents_win32 (const gchar *filename,
gchar **contents,
return get_contents_stdio (filename, f, contents, length, error);
}
+
#endif
/**
return get_contents_posix (filename, contents, length, error);
#endif
}
-
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);
gint n_fds,
gint timeout)
{
- int i;
int result;
- gboolean debug = FALSE;
g_return_val_if_fail (n_fds >= 0, 0);
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
g_error_matches
g_error_new
g_error_new_literal
+ g_file_test
g_filename_from_utf8
g_filename_to_utf8
g_free
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
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
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
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
--- /dev/null
+/* 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 */
*/
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"; /* ??? */
}
config.h \
glibconfig.h \
$(DLLS_TO_BUILD) \
+ gspawn-win32-helper.exe \
testglib.exe \
testgdate.exe \
testgdateparser.exe
gdataset.o \
gdate.o \
gerror.o \
+ gfileutils.o \
ghook.o \
ghash.o \
giochannel.o \
gprimes.o \
gqueue.o \
grand.o \
+ gshell.o \
gslist.o \
+ gspawn-win32.o \
gthread.o \
gthreadpool.o \
gtimer.o \
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
config.h \
glibconfig.h \
$(DLLS_TO_BUILD) \
+ gspawn-win32-helper.exe \
testglib.exe \
testgdate.exe \
testgdateparser.exe
gdataset.obj \
gdate.obj \
gerror.obj \
+ gfileutils.obj \
gconvert.obj \
ghash.obj \
ghook.obj \
grand.obj \
grel.obj \
gscanner.obj \
+ gshell.obj \
gslist.obj \
gstrfuncs.obj \
gstring.obj \
+ gspawn-win32.obj \
gthread.obj \
gthreadpool.obj \
gtimer.obj \
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 :
--- /dev/null
+/* 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 */
*/
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"; /* ??? */
}
config.h \
glibconfig.h \
$(DLLS_TO_BUILD) \
+ gspawn-win32-helper.exe \
testglib.exe \
testgdate.exe \
testgdateparser.exe
gdataset.o \
gdate.o \
gerror.o \
+ gfileutils.o \
ghook.o \
ghash.o \
giochannel.o \
gprimes.o \
gqueue.o \
grand.o \
+ gshell.o \
gslist.o \
+ gspawn-win32.o \
gthread.o \
gthreadpool.o \
gtimer.o \
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
config.h \
glibconfig.h \
$(DLLS_TO_BUILD) \
+ gspawn-win32-helper.exe \
testglib.exe \
testgdate.exe \
testgdateparser.exe
gdataset.obj \
gdate.obj \
gerror.obj \
+ gfileutils.obj \
gconvert.obj \
ghash.obj \
ghook.obj \
grand.obj \
grel.obj \
gscanner.obj \
+ gshell.obj \
gslist.obj \
gstrfuncs.obj \
gstring.obj \
+ gspawn-win32.obj \
gthread.obj \
gthreadpool.obj \
gtimer.obj \
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 :
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 \
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 \
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))
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