[glib/wip/gsubprocess] GSubprocess: WIP



commit cd19b5a0c015b0d2531881a550b756c3a3c8ff20
Author: Colin Walters <walters verbum org>
Date:   Wed May 16 16:58:58 2012 -0400

    GSubprocess: WIP

 gio/giotypes.h           |   10 +
 gio/gmemoryinputstream.c |   88 +++--
 gio/gmemoryinputstream.h |    3 +
 gio/gsubprocess.c        |  912 ++++++++++++++++++++++++++++++++++++++++++++++
 gio/gsubprocess.h        |  174 +++++++++
 5 files changed, 1149 insertions(+), 38 deletions(-)
---
diff --git a/gio/giotypes.h b/gio/giotypes.h
index 876d856..e4f38ec 100644
--- a/gio/giotypes.h
+++ b/gio/giotypes.h
@@ -135,6 +135,16 @@ typedef struct _GIOStream                     GIOStream;
 typedef struct _GPollableInputStream          GPollableInputStream; /* Dummy typedef */
 typedef struct _GPollableOutputStream         GPollableOutputStream; /* Dummy typedef */
 typedef struct _GResolver                     GResolver;
+
+/**
+ * GSubprocess:
+ *
+ * A child process.
+ *
+ * Since: 2.34
+ */
+typedef struct _GSubprocess                   GSubprocess;
+
 /**
  * GResource:
  *
diff --git a/gio/gmemoryinputstream.c b/gio/gmemoryinputstream.c
index dac0ac1..7523044 100644
--- a/gio/gmemoryinputstream.c
+++ b/gio/gmemoryinputstream.c
@@ -44,14 +44,6 @@
  * #GPollableInputStream.
  */
 
-typedef struct _Chunk Chunk;
-
-struct _Chunk {
-  guint8         *data;
-  gsize           len;
-  GDestroyNotify  destroy;
-};
-
 struct _GMemoryInputStreamPrivate {
   GSList *chunks;
   gsize   len;
@@ -140,17 +132,6 @@ g_memory_input_stream_class_init (GMemoryInputStreamClass *klass)
 }
 
 static void
-free_chunk (gpointer data)
-{
-  Chunk *chunk = data;
-
-  if (chunk->destroy)
-    chunk->destroy (chunk->data);
-
-  g_slice_free (Chunk, chunk);
-}
-
-static void
 g_memory_input_stream_finalize (GObject *object)
 {
   GMemoryInputStream        *stream;
@@ -159,7 +140,7 @@ g_memory_input_stream_finalize (GObject *object)
   stream = G_MEMORY_INPUT_STREAM (object);
   priv = stream->priv;
 
-  g_slist_free_full (priv->chunks, free_chunk);
+  g_slist_free_full (priv->chunks, (GDestroyNotify)g_bytes_unref);
 
   G_OBJECT_CLASS (g_memory_input_stream_parent_class)->finalize (object);
 }
@@ -232,6 +213,20 @@ g_memory_input_stream_new_from_data (const void     *data,
 }
 
 /**
+ * g_memory_input_stream_new_from_bytes:
+ * @bytes: a #GBytes
+ *
+ * Creates a new #GMemoryInputStream with data from the given @bytes.
+ * 
+ * Returns: new #GInputStream read from @bytes
+ **/
+GInputStream *
+g_memory_input_stream_new_from_bytes (GBytes  bytes)
+{
+  
+}
+
+/**
  * g_memory_input_stream_add_data:
  * @stream: a #GMemoryInputStream
  * @data: (array length=len) (element-type guint8) (transfer full): input data
@@ -246,24 +241,35 @@ g_memory_input_stream_add_data (GMemoryInputStream *stream,
                                 gssize              len,
                                 GDestroyNotify      destroy)
 {
+  GBytes *bytes;
+
+  bytes = g_bytes_new_with_free_func (data, len, destroy, NULL);
+
+  g_memory_input_stream_add_bytes (stream, bytes);
+  
+  g_bytes_unref (bytes);
+}
+
+/**
+ * g_memory_input_stream_add_bytes:
+ * @stream: a #GMemoryInputStream
+ * @bytes: input data
+ *
+ * Appends @bytes to data that can be read from the input stream.
+ */
+void
+g_memory_input_stream_add_bytes (GMemoryInputStream *stream,
+				 GBytes              bytes)
+{
   GMemoryInputStreamPrivate *priv;
-  Chunk *chunk;
  
   g_return_if_fail (G_IS_MEMORY_INPUT_STREAM (stream));
   g_return_if_fail (data != NULL);
 
   priv = stream->priv;
 
-  if (len == -1)
-    len = strlen (data);
-  
-  chunk = g_slice_new (Chunk);
-  chunk->data = (guint8 *)data;
-  chunk->len = len;
-  chunk->destroy = destroy;
-
-  priv->chunks = g_slist_append (priv->chunks, chunk);
-  priv->len += chunk->len;
+  priv->chunks = g_slist_append (priv->chunks, g_bytes_ref (bytes));
+  priv->len += g_bytes_get_size (bytes);
 }
 
 static gssize
@@ -276,7 +282,8 @@ g_memory_input_stream_read (GInputStream  *stream,
   GMemoryInputStream *memory_stream;
   GMemoryInputStreamPrivate *priv;
   GSList *l;
-  Chunk *chunk;
+  GBytes *chunk;
+  gsize len;
   gsize offset, start, rest, size;
 
   memory_stream = G_MEMORY_INPUT_STREAM (stream);
@@ -287,12 +294,13 @@ g_memory_input_stream_read (GInputStream  *stream,
   offset = 0;
   for (l = priv->chunks; l; l = l->next) 
     {
-      chunk = (Chunk *)l->data;
+      chunk = (GBytes *)l->data;
+      len = g_bytes_get_size (chunk);
 
-      if (offset + chunk->len > priv->pos)
+      if (offset + len > priv->pos)
         break;
 
-      offset += chunk->len;
+      offset += len;
     }
   
   start = priv->pos - offset;
@@ -300,10 +308,14 @@ g_memory_input_stream_read (GInputStream  *stream,
 
   for (; l && rest > 0; l = l->next)
     {
-      chunk = (Chunk *)l->data;
-      size = MIN (rest, chunk->len - start);
+      guint8*chunk_data;
+      chunk = (GBytes *)l->data;
+
+      chunk_data = g_bytes_get_data (chunk, &len);
+
+      size = MIN (rest, len - start);
 
-      memcpy ((guint8 *)buffer + (count - rest), chunk->data + start, size);
+      memcpy ((guint8 *)buffer + (count - rest), chunk_data + start, size);
       rest -= size;
 
       start = 0;
diff --git a/gio/gmemoryinputstream.h b/gio/gmemoryinputstream.h
index e629db1..de881f6 100644
--- a/gio/gmemoryinputstream.h
+++ b/gio/gmemoryinputstream.h
@@ -72,10 +72,13 @@ GInputStream * g_memory_input_stream_new           (void);
 GInputStream * g_memory_input_stream_new_from_data (const void         *data,
                                                     gssize              len,
                                                     GDestroyNotify      destroy);
+GInputStream * g_memory_input_stream_new_from_bytes (GBytes             bytes);
 void           g_memory_input_stream_add_data      (GMemoryInputStream *stream,
                                                     const void         *data,
                                                     gssize              len,
                                                     GDestroyNotify      destroy);
+void           g_memory_input_stream_add_bytes     (GMemoryInputStream     *stream,
+						    GBytes                  bytes);
 
 G_END_DECLS
 
diff --git a/gio/gsubprocess.c b/gio/gsubprocess.c
new file mode 100644
index 0000000..91f7f8d
--- /dev/null
+++ b/gio/gsubprocess.c
@@ -0,0 +1,912 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright  2012 Red Hat, Inc.
+ *
+ * This program 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 licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ */
+
+/**
+ * SECTION:gsubprocess
+ * @title: GSubprocess
+ * @short_description: Create child processes
+ *
+ * This class is primarily convenience API on top of the lower-level
+ * g_spawn_async_with_pipes() and related functions provided by GLib.
+ * 
+ * Since: 2.34
+ */
+
+#include "config.h"
+#include "gsubprocess.h"
+#include "gasyncresult.h"
+#include "giostream.h"
+#include "glibintl.h"
+
+typedef struct _GSubprocessClass GSubprocessClass;
+
+typedef enum {
+  G_SUBPROCESS_STATE_BUILDING,
+  G_SUBPROCESS_STATE_RUNNING,
+  G_SUBPROCESS_STATE_TERMINATED
+} GSubprocessState;
+
+struct _GSubprocess
+{
+  GSubprocessState state;
+
+  gchar *child_argv0;
+  GPtrArray *child_argv;
+  gchar **child_envp;
+
+  gboolean search_path : 1;
+  gboolean leave_descriptors_open : 1;
+  gboolean stdin_to_devnull : 1;
+  gboolean stdout_to_devnull : 1;
+  gboolean stderr_to_devnull : 1;
+
+  gchar *working_directory;
+
+  GSpawnChildSetupFunc child_setup;
+  gpointer             child_setup_user_data;
+
+  gint stdin_fd;
+  gchar *stdin_path;
+  GInputStream *stdin_stream;
+  gchar *stdout_path;
+  GInputStream *stdout_stream;
+  gchar *stderr_path;
+  GInputStream *stderr_stream;
+
+  GPid pid;
+}
+
+G_DEFINE_TYPE (GSubprocess, g_subprocess, G_TYPE_OBJECT);
+
+enum
+{
+  PROP_0
+};
+
+static void
+g_subprocess_init (GSubprocess  *self)
+{
+  self->state = G_SUBPROCESS_STATE_BUILDING;
+  self->child_argv = g_ptr_array_new_with_free_func (g_free);
+  self->search_path = TRUE;
+  self->stdin_to_devnull = TRUE;
+}
+
+static void
+g_subprocess_dispose (GObject *object)
+{
+  GSubprocess *self = G_SUBPROCESS (object);
+
+  g_clear_object (&self->stdin_stream);
+  g_clear_object (&self->stdout_stream);
+  g_clear_object (&self->stderr_stream);
+
+  if (G_OBJECT_CLASS (g_subprocess_parent_class)->dispose != NULL)
+    G_OBJECT_CLASS (g_subprocess_parent_class)->dispose (object);
+}
+
+static void
+g_subprocess_finalize (GObject *object)
+{
+  GSubprocess *self = G_SUBPROCESS (object);
+
+  if (self->child_argv)
+    g_ptr_array_unref (self->child_argv);
+
+  g_strfreev (self->child_envp);
+
+  g_free (self->working_directory);
+}
+
+static void
+g_subprocess_class_init (GTcpConnectionClass *class)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+  gobject_class->dispose = g_subprocess_dispose;
+  gobject_class->finalize = g_subprocess_finalize;
+}
+
+/**** Creation ****/
+
+/**
+ * g_subprocess_new:
+ *
+ * After calling this function, you must use one of the functions to
+ * initialize the argument vector, such as g_subprocess_set_argv().
+ * 
+ * Returns: A new #GSubprocess
+ */
+GSubprocess *
+g_subprocess_new (void)
+{
+  return g_object_new (G_TYPE_SUBPROCESS, NULL);
+}
+
+/**
+ * g_subprocess_new_with_args:
+ * @first: (array zero-terminated=1) (element-type guint8): First argument
+ *
+ * See the documentation of g_subprocess_append_args_va() for details
+ * about child process arguments.
+ *
+ * After calling this function, you may append further arguments with
+ * e.g. g_subprocess_append_args().
+ * 
+ * Returns: A new #GSubprocess with the provided arguments
+ */
+GSubprocess *
+g_subprocess_new_with_args (const gchar     *first,
+			    ...)
+{
+  va_list args;
+  GSubprocess *ret;
+
+  ret = g_subprocess_new ();
+  
+  va_start (args, first);
+  g_subprocess_append_args_va (ret, args);
+  va_end (args);
+  
+  return ret;
+}
+
+/**** Argument control ****/
+
+/**
+ * g_subprocess_set_argv:
+ * @self: a #GSubprocess:
+ * @argv: (array zero-terminated=1): Argument array, %NULL-terminated
+ *
+ * Set the arguments to be used by the child process.  This will
+ * overwrite any previous calls to g_subprocess_append_args().
+ *
+ * Due to limitations of gobject-introspection, the provided argument
+ * is annotated as an array of strings, not an array of bytestrings.
+ *
+ * For language bindings, you can use calls to
+ * g_subprocess_append_arg() indivdually to provide non-UTF8
+ * arguments.
+ *
+ * For more information, see g_subprocess_append_args_va().
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_set_argv (GSubprocess          *self,
+		       gchar               **argv)
+{
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (argv != NULL);
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+
+  g_ptr_array_set_size (self->child_argv, 0);
+  
+  for (; *argv; argv++)
+    g_ptr_array_add (self->child_argv, g_strdup (*argv));
+}
+
+/**
+ * g_subprocess_set_argv0:
+ * @self: a #GSubprocess:
+ * @argv: (array zero-terminated=1) (element-type guint8): First argument
+ *
+ * On Unix, child processes receive a bytestring provided by the
+ * parent process as the first argument (i.e. argv[0]).  By default,
+ * GLib will set this to whatever executable is run, but you may override
+ * it with this function.
+ *
+ * For example, some implementations of Unix have just one 'grep'
+ * executable which behaves differently when executed as 'grep' or
+ * 'fgrep'.
+ *
+ * See the discussion of %G_SPAWN_FILE_AND_ARGV_ZERO in the
+ * g_spawn_async_with_pipes() documentation.
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_set_argv0 (GSubprocess         *self,
+			const gchar         *argv0)
+{
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+
+  g_free (self->child_argv0);
+  self->child_argv0 = g_strdup (argv0);
+}
+
+/**
+ * g_subprocess_append_arg:
+ * @self: a #GSubprocess:
+ * @arg: (array zero-terminated=1) (element-type guint8): Argument
+ *
+ * Append an argument to the child process.  On Unix, this argument is
+ * a bytestring.  For more information, see
+ * g_subprocess_append_args_va().
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_append_arg (GSubprocess       *self,
+			 const gchar       *arg)
+{
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+
+  g_ptr_array_add (self->child_argv, g_strdup (arg));
+}
+
+
+/**
+ * g_subprocess_append_args:
+ * @self: a #GSubprocess:
+ * @first: (array zero-terminated=1) (element-type guint8): First argument to be appended
+ *
+ * Appends arguments to the child process.  For more information, see
+ * g_subprocess_append_args_va().
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_append_args (GSubprocess       *self,
+			  const gchar       *first,
+			  ...) 
+{
+  va_list args;
+
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (first != NULL);
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+  
+  g_subprocess_append_arg (self, first);
+
+  va_start (args, first);
+  g_subprocess_append_args_va (self, args);
+  va_end (args);
+}
+
+/**
+ * g_subprocess_append_args_va:
+ * @self: a #GSubprocess
+ * @args: List of %NULL-terminated arguments
+ *
+ * Append the provided @args to the child argument vector. 
+ *
+ * On Unix, the arguments are bytestrings.  Note though that while
+ * it's possible to pass arbitrary bytestrings to subprocesses on
+ * Unix, not all utility programs are written to handle this
+ * correctly.  You should typically only pass strings which are valid
+ * in the current locale encoding; see g_locale_to_utf8().
+ *
+ * On Windows, the arguments are in the GLib filename encoding
+ * (i.e. UTF-8).
+ *
+ * For more information, about how arguments are processed, see the
+ * documentation of g_spawn_async_with_pipes().
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_append_args_va (GSubprocess       *self,
+			     va_list            args)
+{
+  GPtrArray *child_argv = NULL;
+  GSubprocess *ret;
+  const char *arg;
+
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+
+  va_start (args, first);
+  
+  arg = first;
+  do
+    {
+      g_ptr_array_add (self->child_argv, g_strdup (arg));
+    } while ((arg = va_arg (args, const char *)) != NULL);
+
+  va_end (args);
+}
+
+/**** GSpawnFlags wrappers ****/
+
+/**
+ * g_subprocess_set_search_path:
+ * @self: a #GSubprocess
+ * @do_search_path: %TRUE if the system path should be searched
+ *
+ * Unlike the g_spawn_async_with_pipes() and related functions,
+ * the default for #GSubprocess is to search the system path.
+ *
+ * See the documentation for %G_SPAWN_SEARCH_PATH for details; setting
+ * @do_search_path to %FALSE is equivalent to not providing that flag.
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_set_search_path (GSubprocess     *self,
+			      gboolean         do_search_path)
+{
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+  
+  self->search_path = do_search_path;
+}
+
+/**
+ * g_subprocess_set_leave_descriptors_open:
+ * @self: a #GSubprocess
+ * @do_leave_descriptors_open: %TRUE if file descriptors should be left open
+ *
+ * See the documentation for %G_SPAWN_LEAVE_DESCRIPTORS_OPEN for
+ * details.
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_set_search_path (GSubprocess     *self,
+			      gboolean         do_leave_descriptors_open)
+{
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+  
+  self->leave_descriptors_open = do_leave_descriptors_open;
+}
+
+/**** Envronment control ****/
+
+/**
+ * g_subprocess_setenv:
+ * @self: a #GSubprocess
+ * @variable: the environment variable to set, must not contain '='
+ * @value: the value for to set the variable to
+ * @overwrite: whether to change the variable if it already exists
+ *
+ * This modifies the environment child process will be run in; it has
+ * no effect on the current process.  See the documentation of
+ * g_environ_setenv() for more information about environment
+ * variables.
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_setenv (GSubprocess       *self,
+		     gchar             *variable,
+		     gchar             *value,
+		     gboolean           overwrite)
+{
+  gchar **new_envp;
+
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+  g_return_if_fail (variable != NULL);
+
+  if (self->child_envp == NULL)
+    self->child_envp = g_get_environ ();
+  
+  new_envp = g_environ_setenv (self->child_envp, variable, value, overwrite);
+  g_strfreev (self->child_envp);
+  self->child_envp = new_envp;
+}
+
+/**
+ * g_subprocess_unsetenv:
+ * @self: a #GSubprocess
+ * @variable: the environment variable to set, must not contain '='
+ *
+ * This modifies the environment child process will be run in; it has
+ * no effect on the current process.  See the documentation of
+ * g_environ_unsetenv() for more information about environment
+ * variables.
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_unsetenv (GSubprocess       *self,
+		       const gchar       *variable)
+{
+  gchar **new_envp;
+
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+  g_return_if_fail (variable != NULL);
+
+  if (self->child_envp == NULL)
+    self->child_envp = g_get_environ ();
+  
+  new_envp = g_environ_unsetenv (self->child_envp, variable);
+  g_strfreev (self->child_envp);
+  self->child_envp = new_envp;
+}
+
+/**
+ * g_subprocess_override_environ:
+ * @self: a #GSubprocess
+ * @envp: (array zero-terminated=1): An environment list
+ *
+ * This completely replaces the environment child process will be run
+ * in; it has no effect on the current process.  See the documentation
+ * of g_environ_setenv() for more information about environment
+ * variables.
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_override_environ (GSubprocess       *self,
+			       gchar            **envp)
+{
+  gchar **new_envp;
+
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+  g_return_if_fail (envp != NULL);
+
+  g_strfreev (self->child_envp);
+  self->child_envp = g_strdupv (envp);
+}
+
+/**
+ * g_subprocess_set_working_directory:
+ * @self: a #GSubprocess
+ * @working_directory: (allow-none): Path to working directory, in the GLib file name encoding
+ *
+ * By default, the child process will inherit the working
+ * directory of the current process.  This function allows
+ * overriding that default.
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_set_working_directory (GSubprocess       *self,
+				    const gchar       *working_directory)
+{
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+
+  g_free (self->working_directory);
+  self->working_directory = g_strdup (working_directory);
+}
+
+/**
+ * g_subprocess_set_child_setup:
+ * @self: a #GSubprocess
+ * @child_setup: (allow-none): Function to run in the context of just-forked child
+ *
+ * This functionality is only available on Unix.  See the
+ * documentation of g_spawn_async_with_pipes() for more information
+ * about @child_setup.
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_set_child_setup (GSubprocess           *self,
+			      GSpawnChildSetupFunc   child_setup,
+			      gpointer               user_data)
+{
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+
+  self->child_setup = child_setup;
+  self->child_setup_user_data = user_data;
+}
+
+/**** Input and Output ****/
+
+/**
+ * g_subprocess_set_standard_input_file_path:
+ * @self: a #GSubprocess
+ * @file_path: String containing path to file to use as standard input
+ *
+ * This function allows providing a file as input to the given
+ * subprocess.  The file will not be opened until g_subprocess_start()
+ * has been called.
+ *
+ * Calling this function overrides any previous calls, as well as
+ * other related functions such as
+ * g_subprocess_set_standard_input_unix_fd(),
+ * g_subprocess_set_standard_input_stream(), and
+ * g_subprocess_set_standard_input_to_devnull().
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_set_standard_input_file_path (GSubprocess       *self,
+					   const gchar       *file_path)
+{
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+  g_return_if_fail (file_path != NULL);
+
+  g_clear_object (&self->stdin_stream);
+  self->stdin_to_devnull = FALSE;
+  self->stdin_fd = -1;
+
+  g_free (self->stdin_path);
+  self->stdin_path = g_strdup (file_path);
+}
+
+/**
+ * g_subprocess_set_standard_input_unix_fd:
+ * @self: a #GSubprocess
+ * @fd: File descriptor to use as standard input
+ *
+ * This function allows providing a file descriptor as input to the given
+ * subprocess.
+ *
+ * Calling this function overrides any previous calls, as well as
+ * other related functions such as
+ * g_subprocess_set_standard_input_file_path().
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_set_standard_input_unix_fd (GSubprocess       *self,
+					 gint               fd)
+{
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+
+  g_clear_object (&self->stdin_stream);
+  g_free (self->stdin_path);
+  self->stdin_path = NULL;
+  self->stdin_to_devnull = FALSE;
+
+  self->stdin_fd = fd;
+}
+
+/**
+ * g_subprocess_set_standard_input_to_devnull:
+ * @self: a #GSubprocess
+ * @to_devnull: If %TRUE, redirect input from null stream, if %FALSE, inherit
+ *
+ * The default is for child processes to have their input stream
+ * pointed at a null stream (e.g. on Unix, /dev/null), because having
+ * multiple processes read from an input stream creates race
+ * conditions and is generally nonsensical.  See the documentation of
+ * g_spawn_async_with_pipes() and %G_SPAWN_CHILD_INHERITS_STDIN.
+ *
+ * If @to_devnull is %FALSE, then this function will cause the
+ * standard input of the child process to be inherited.
+ *
+ * Calling this function overrides any previous calls, as well as
+ * other related functions such as
+ * g_subprocess_set_standard_input_file_path().
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_set_standard_input_to_devnull (GSubprocess       *self,
+					    gboolean           to_devnull)
+{
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+
+  g_clear_object (&self->stdin_stream);
+  g_free (self->stdin_path);
+  self->stdin_path = NULL;
+  self->stdin_fd = -1;
+
+  self->stdin_to_devnull = to_devnull;
+}
+
+/**
+ * g_subprocess_set_standard_input_stream:
+ * @self: a #GSubprocess
+ * @stream: an input stream
+ *
+ * Use the provided stream as input to the child process.  The stream
+ * will not be closed upon completion by default; however, you may
+ * safely drop other references to @stream, and then it will be
+ * automatically closed when @self itself is unreferenced.
+ *
+ * <note>
+ *  If your input stream is from a file, such as that returned
+ *  by g_file_read(), it is significantly more efficient
+ *  to cause the child process to read the file directly
+ *  via g_subprocess_set_standard_input_file_path().
+ * </note>
+ *
+ * Calling this function overrides any previous calls, as well as
+ * other related functions such as
+ * g_subprocess_set_standard_input_file_path().
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_set_standard_input_stream (GSubprocess       *self,
+					GInputStream      *stream)
+{
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+  g_return_if_fail (stream != NULL);
+
+  g_free (self->stdin_path);
+  self->stdin_path = NULL;
+  self->stdin_fd = -1;
+  self->stdin_to_devnull = FALSE;
+
+  g_clear_object (&self->stdin_stream);
+  self->stdin_stream = g_object_ref (stream);
+}
+
+/**
+ * g_subprocess_set_standard_input_bytes:
+ * @self: a #GSubprocess
+ * @buf: Buffer to use as input
+ *
+ * Use the provided data as input to the child process.  This
+ * function simply wraps g_subprocess_set_standard_input_stream()
+ * using g_memory_input_stream_new_from_bytes().
+ *
+ * Calling this function overrides any previous calls, as well as
+ * other related functions such as
+ * g_subprocess_set_standard_input_file_path().
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_set_standard_input_bytes (GSubprocess       *self,
+				       GBytes            *buf)
+{
+  GMemoryInputStream *stream;
+
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+  g_return_if_fail (buf != NULL);
+
+  stream = g_memory_input_stream_new_from_bytes (buf);
+  g_subprocess_set_standard_input_stream (self, stream);
+  g_object_unref (stream);
+}
+
+/**
+ * g_subprocess_set_standard_input_str:
+ * @self: a #GSubprocess
+ * @str: (array zero-terminated=1) (element-type guint8): Buffer to use as input
+ *
+ * Use the provided data as input to the child process.  This function
+ * simply wraps g_subprocess_set_standard_input_bytes().
+ *
+ * Calling this function overrides any previous calls, as well as
+ * other related functions such as
+ * g_subprocess_set_standard_input_file_path().
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_set_standard_input_str (GSubprocess       *self,
+				     const gchar       *str)
+{
+  GBytes *bytes;
+
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+  g_return_if_fail (str != NULL);
+
+  bytes = g_bytes_new (str, strlen (str));
+  g_subprocess_set_standard_input_bytes (self, bytes);
+  g_bytes_unref (bytes);
+}
+
+/**
+ * g_subprocess_set_standard_output_to_devnull:
+ * @self: a #GSubprocess
+ * @to_devnull: If %TRUE, redirect process output to null stream
+ *
+ * The default is for the child process to inherit the standard output
+ * of the current process.  Specify %TRUE for @to_devnull to redirect it
+ * to the operating system's null stream.
+ *
+ * Calling this function overrides any previous calls, as well as
+ * other related functions such as
+ * g_subprocess_set_standard_output_file_path().
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_set_standard_output_to_devnull (GSubprocess       *self,
+					     gboolean           to_devnull)
+{
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+
+  g_free (self->stdout_path);
+  self->stdout_path = NULL;
+  self->stdout_fd = -1;
+  g_clear_object (&self->stdout_stream);
+
+  self->stdout_to_devnull = to_devnull;
+}
+
+/**
+ * g_subprocess_set_standard_output_file_path:
+ * @self: a #GSubprocess
+ * @file_path: String containing path to file to use as standard input
+ *
+ * This function allows providing a file as standard output for the
+ * given subprocess.  The file will not be opened until
+ * g_subprocess_start() has been called.
+ *
+ * Calling this function overrides any previous calls, as well as
+ * other related functions such as
+ * g_subprocess_set_standard_output_to_devnull().
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_set_standard_output_file_path (GSubprocess       *self,
+					    const gchar       *file_path)
+{
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+
+  g_free (self->stdout_path);
+  self->stdout_path = NULL;
+  self->stdout_fd = -1;
+  g_clear_object (&self->stdout_stream);
+
+  self->stdout_to_devnull = to_devnull;
+}
+
+/**
+ * g_subprocess_set_standard_output_unix_fd:
+ * @self: a #GSubprocess
+ * @fd: File descriptor
+ *
+ * This function allows providing a file descriptor as standard output
+ * for the given subprocess.
+ *
+ * Calling this function overrides any previous calls, as well as
+ * other related functions such as
+ * g_subprocess_set_standard_output_file_path().
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_set_standard_output_unix_fd (GSubprocess       *self,
+					  gint               fd)
+{
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+
+  g_free (self->stdout_path);
+  self->stdout_path = NULL;
+  self->stdout_to_devnull = FALSE;
+  g_clear_object (&self->stdout_stream);
+
+  self->stdout_fd = fd;
+}
+
+/**
+ * g_subprocess_set_standard_error_to_devnull:
+ * @self: a #GSubprocess
+ * @to_devnull: If %TRUE, redirect process error output to null stream
+ *
+ * The default is for the child process to inherit the standard error
+ * of the current process.  Specify %TRUE for @to_devnull to redirect
+ * it to the operating system's null stream.
+ *
+ * Calling this function overrides any previous calls, as well as
+ * other related functions such as
+ * g_subprocess_set_standard_error_file_path().
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_set_standard_error_to_devnull (GSubprocess       *self,
+					    gboolean           to_devnull)
+{
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+
+  g_free (self->stderr_path);
+  self->stderr_path = NULL;
+  self->stderr_fd = -1;
+  g_clear_object (&self->stderr_stream);
+
+  self->stderr_to_devnull = to_devnull;
+}
+
+/**
+ * g_subprocess_set_standard_error_file_path:
+ * @self: a #GSubprocess
+ * @file_path: String containing path to file to use as standard input
+ *
+ * This function allows providing a file as standard error for the
+ * given subprocess.  The file will not be opened until
+ * g_subprocess_start() has been called.
+ *
+ * Calling this function overrides any previous calls, as well as
+ * other related functions such as
+ * g_subprocess_set_standard_error_to_devnull().
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_set_standard_error_file_path (GSubprocess       *self,
+					    const gchar       *file_path)
+{
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+
+  g_free (self->stderr_path);
+  self->stderr_path = NULL;
+  self->stderr_fd = -1;
+  g_clear_object (&self->stderr_stream);
+
+  self->stderr_to_devnull = to_devnull;
+}
+
+/**
+ * g_subprocess_set_standard_error_unix_fd:
+ * @self: a #GSubprocess
+ * @fd: File descriptor
+ *
+ * This function allows providing a file descriptor as standard error
+ * for the given subprocess.
+ *
+ * Calling this function overrides any previous calls, as well as
+ * other related functions such as
+ * g_subprocess_set_standard_error_file_path().
+ *
+ * It invalid to call this function after g_subprocess_start() has
+ * been called.
+ */
+void
+g_subprocess_set_standard_error_unix_fd (GSubprocess       *self,
+					  gint               fd)
+{
+  g_return_if_fail (G_IS_SUBPROCESS (self));
+  g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
+
+  g_free (self->stderr_path);
+  self->stderr_path = NULL;
+  self->stderr_to_devnull = FALSE;
+  g_clear_object (&self->stderr_stream);
+
+  self->stderr_fd = fd;
+}
+
+GInputStream *
+g_subprocess_request_standard_output (GSubprocess  *self)
+{
+
+}
+
+GInputStream *
+g_subprocess_request_standard_error (GSubprocess  *self)
+{
+}
diff --git a/gio/gsubprocess.h b/gio/gsubprocess.h
new file mode 100644
index 0000000..be9064d
--- /dev/null
+++ b/gio/gsubprocess.h
@@ -0,0 +1,174 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2012 Colin Walters <walters verbum org>
+ *
+ * This library 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.
+ *
+ * This library 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 this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters verbum org>
+ */
+
+#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
+#error "Only <gio/gio.h> can be included directly."
+#endif
+
+#ifndef __G_SUBPROCESS_H__
+#define __G_SUBPROCESS_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_SUBPROCESS         (g_subprocess_get_type ())
+#define G_SUBPROCESS(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_SUBPROCESS, GSubprocess))
+#define G_IS_SUBPROCESS(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_SUBPROCESS))
+
+GType            g_subprocess_get_type (void) G_GNUC_CONST;
+
+/**** Creation ****/
+
+GSubprocess *    g_subprocess_new (void);
+
+GSubprocess *    g_subprocess_new_with_args (const gchar     *first,
+					     ...) G_GNUC_NULL_TERMINATED;
+
+/**** Argument control ****/
+
+void             g_subprocess_set_argv (GSubprocess          *self,
+					gchar               **argv);
+
+void             g_subprocess_set_argv0 (GSubprocess         *self,
+					 const gchar         *argv0);
+
+void             g_subprocess_append_arg (GSubprocess       *self,
+					  const gchar       *arg);
+
+void             g_subprocess_append_args (GSubprocess       *self,
+					   const gchar       *first,
+					   ...) G_GNUC_NULL_TERMINATED;
+
+void             g_subprocess_append_args_va (GSubprocess       *self,
+					      va_list            args);
+
+/**** Envronment control ****/
+
+void             g_subprocess_set_search_path (GSubprocess     *self,
+					       gboolean         do_search_path);
+
+void             g_subprocess_set_leave_descriptors_open (GSubprocess     *self,
+							  gboolean         do_leave_descriptors_open);
+
+void             g_subprocess_setenv (GSubprocess       *self,
+				      gchar             *variable,
+				      gchar             *value,
+				      gboolean           overwrite);
+
+void             g_subprocess_unsetenv (GSubprocess       *self,
+					const gchar       *variable);
+
+void             g_subprocess_override_environ (GSubprocess       *self,
+						gchar            **envp);
+
+void             g_subprocess_set_working_directory (GSubprocess       *self,
+						     const gchar       *working_directory);
+
+void             g_subprocess_set_child_setup (GSubprocess           *self,
+					       GSpawnChildSetupFunc   child_setup,
+					       gpointer               user_data);
+
+/**** Input and Output ****/
+
+void             g_subprocess_set_standard_input_file_path (GSubprocess       *self,
+							    const gchar       *file_path);
+
+#ifdef G_OS_UNIX
+void             g_subprocess_set_standard_input_unix_fd (GSubprocess       *self,
+							  gint               fd);
+#endif
+
+void             g_subprocess_set_standard_input_to_devnull (GSubprocess       *self,
+							     gboolean           to_devnull);
+
+void             g_subprocess_set_standard_input_stream (GSubprocess       *self,
+							 GInputStream      *stream);
+
+void             g_subprocess_set_standard_input_bytes (GSubprocess       *self,
+							GBytes            *buf);
+
+void             g_subprocess_set_standard_input_str (GSubprocess       *self,
+						      const gchar       *str);
+
+void             g_subprocess_set_standard_output_to_devnull (GSubprocess       *self,
+							      gboolean           to_devnull);
+
+void             g_subprocess_set_standard_output_file_path (GSubprocess       *self,
+							     const gchar       *file_path);
+
+#ifdef G_OS_UNIX
+void             g_subprocess_set_standard_output_unix_fd (GSubprocess       *self,
+							   gint               fd);
+#endif
+
+void             g_subprocess_set_standard_error_to_devnull (GSubprocess       *self,
+							     gboolean           to_devnull);
+
+void             g_subprocess_set_standard_error_file_path (GSubprocess       *self,
+							    const gchar       *file_path);
+
+#ifdef G_OS_UNIX
+void             g_subprocess_set_standard_error_unix_fd (GSubprocess       *self,
+							  gint               fd);
+#endif
+
+GInputStream    *g_subprocess_request_standard_output (GSubprocess  *self);
+GInputStream    *g_subprocess_request_standard_error (GSubprocess  *self);
+
+/**** Running ****/
+
+gboolean         g_subprocess_start (GSubprocess       *self,
+				     GError           **error);
+
+GPid             g_subprocess_get_pid (GSubprocess     *self);
+
+void             g_subprocess_wait_async (GSubprocess        *self,
+					  GCancellable       *cancellable,
+					  GAsyncReadyCallback callback,
+					  gpointer            user_data);
+
+void             g_subprocess_wait_async_finish (GSubprocess        *self,
+						 GAsyncResult       *result,
+						 GError            **error);
+
+gboolean         g_subprocess_wait_sync (GSubprocess   *self,
+					 GCancellable  *cancellable,
+					 GError       **error);
+
+gint             g_subprocess_get_exit_code (GSubprocess   *self);
+
+/**** Static utility functions ****/
+
+gboolean         g_subprocess_spawn_sync_get_output_utf8 (gchar        **argv,
+							  gchar        **output_utf8,
+							  GCancellable  *cancellable,
+							  GError       **error);
+
+gboolean         g_subprocess_spawn_sync_get_output_bytes (gchar               **argv,
+							   GBytes              **output_bytes,
+							   GCancellable         *cancellable,
+							   GError              **error);
+
+G_END_DECLS
+
+#endif /* __G_SUBPROCESS_H__ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]