[gedit/dbus: 3/5] Implement loading document from stdin



commit 8ecf21b3454b99751e8c5dd6401b962accc46f69
Author: Jesse van den Kieboom <jesse vandenkieboom epfl ch>
Date:   Wed May 5 23:21:28 2010 +0200

    Implement loading document from stdin

 gedit/Makefile.am             |    5 +
 gedit/gedit-dbus.c            |  605 +++++++++++++++++++++++++++++++++++++++--
 gedit/gedit-document-loader.c |   18 ++
 gedit/gedit-fifo.c            |  347 +++++++++++++++++++++++
 gedit/gedit-fifo.h            |   58 ++++
 gedit/gedit-utils.c           |   15 +
 gedit/gedit-utils.h           |    2 +
 gedit/gedit.c                 |   23 +-
 8 files changed, 1029 insertions(+), 44 deletions(-)
---
diff --git a/gedit/Makefile.am b/gedit/Makefile.am
index 838e03e..bfc9693 100644
--- a/gedit/Makefile.am
+++ b/gedit/Makefile.am
@@ -216,6 +216,11 @@ if !ENABLE_GVFS_METADATA
 libgedit_la_SOURCES += gedit-metadata-manager.c
 endif
 
+if !PLATFORM_WIN32
+libgedit_la_SOURCES += gedit-fifo.c
+NOINST_H_FILES += gedit-fifo.h
+endif
+
 gedit-enum-types.h: gedit-enum-types.h.template $(INST_H_FILES) $(GLIB_MKENUMS)
 	$(AM_V_GEN) (cd $(srcdir) && $(GLIB_MKENUMS) --template gedit-enum-types.h.template $(INST_H_FILES)) > $@
 
diff --git a/gedit/gedit-dbus.c b/gedit/gedit-dbus.c
index 3b63391..6e0d8e4 100644
--- a/gedit/gedit-dbus.c
+++ b/gedit/gedit-dbus.c
@@ -35,6 +35,13 @@
 #include <gdk/gdkx.h>
 #endif
 
+#ifdef G_OS_UNIX
+#include <gio/gunixinputstream.h>
+#include <gio/gunixconnection.h>
+#include <unistd.h>
+#include "gedit-fifo.h"
+#endif
+
 typedef struct _WaitData WaitData;
 typedef void (*WaitHandlerFunc)(GObject *object, WaitData *data);
 
@@ -51,6 +58,19 @@ struct _WaitData
 
 #define GEDIT_DBUS_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GEDIT_TYPE_DBUS, GeditDBusPrivate))
 
+typedef struct
+{
+	GeditDBus *dbus;
+	GCancellable *cancellable;
+
+	GeditWindow *window;
+	const GeditEncoding *encoding;
+	gint line_position;
+	gint column_position;
+	gboolean jump_to;
+	WaitData *wait_data;
+} AsyncData;
+
 struct _GeditDBusPrivate
 {
 	GeditDBusResult result;
@@ -58,13 +78,79 @@ struct _GeditDBusPrivate
 	guint wait_id;
 
 	guint next_wait_id;
+
+	GeditFifo *stdin_fifo;
+	GInputStream *stdin_in_stream;
+	GOutputStream *stdin_out_stream;
+	GCancellable *stdin_cancellable;
 };
 
 G_DEFINE_TYPE (GeditDBus, gedit_dbus, G_TYPE_OBJECT)
 
 static void
+async_window_destroyed (AsyncData *async,
+                        GObject   *where_the_object_was)
+{
+	g_cancellable_cancel (async->cancellable);
+	async->window = NULL;
+}
+
+static void
+async_data_free (AsyncData *async)
+{
+	g_object_unref (async->cancellable);
+
+	if (async->window)
+	{
+		g_object_weak_unref (G_OBJECT (async->window),
+		                     (GWeakNotify)async_window_destroyed,
+		                     async);
+	}
+
+	g_slice_free (AsyncData, async);
+}
+
+static AsyncData *
+async_data_new (GeditDBus *dbus)
+{
+	AsyncData *async;
+
+	async = g_slice_new0 (AsyncData);
+
+	async->dbus = dbus;
+	async->cancellable = g_cancellable_new ();
+
+	dbus->priv->stdin_cancellable = g_object_ref (async->cancellable);
+
+	return async;
+}
+
+static void
 gedit_dbus_finalize (GObject *object)
 {
+	GeditDBus *dbus = GEDIT_DBUS (object);
+
+	if (dbus->priv->stdin_cancellable)
+	{
+		g_cancellable_cancel (dbus->priv->stdin_cancellable);
+		g_object_unref (dbus->priv->stdin_cancellable);
+	}
+
+	if (dbus->priv->stdin_fifo)
+	{
+		g_object_unref (dbus->priv->stdin_fifo);
+	}
+
+	if (dbus->priv->stdin_out_stream)
+	{
+		g_object_unref (dbus->priv->stdin_out_stream);
+	}
+
+	if (dbus->priv->stdin_in_stream)
+	{
+		g_object_unref (dbus->priv->stdin_in_stream);
+	}
+
 	G_OBJECT_CLASS (gedit_dbus_parent_class)->finalize (object);
 }
 
@@ -197,7 +283,7 @@ get_display_arguments (GeditDBus         *dbus,
 }
 
 static GVariant *
-compose_open_parameters (GeditDBus *dbus, GeditCommandLine *command_line)
+compose_open_parameters (GeditDBus *dbus)
 {
 	GVariantBuilder file_list;
 	GSList *files;
@@ -208,6 +294,10 @@ compose_open_parameters (GeditDBus *dbus, GeditCommandLine *command_line)
 	gint viewport_x;
 	gint viewport_y;
 	GVariant *ret;
+	GeditCommandLine *command_line;
+	gchar *stdin_path;
+
+	command_line = gedit_command_line_get_default ();
 
 	g_variant_builder_init (&file_list, G_VARIANT_TYPE ("as"));
 
@@ -228,7 +318,18 @@ compose_open_parameters (GeditDBus *dbus, GeditCommandLine *command_line)
 	                       &viewport_x,
 	                       &viewport_y);
 
-	ret = g_variant_new ("(assiibbbusiiii)",
+	if (dbus->priv->stdin_fifo)
+	{
+		GFile *file = gedit_fifo_get_file (dbus->priv->stdin_fifo);
+		stdin_path = g_file_get_path (file);
+		g_object_unref (file);
+	}
+	else
+	{
+		stdin_path = g_strdup ("");
+	}
+
+	ret = g_variant_new ("(assiibbbusiiiis)",
 	                     &file_list,
 	                     encoding ? gedit_encoding_get_charset (encoding) : "",
 	                     gedit_command_line_get_line_position (command_line),
@@ -241,23 +342,29 @@ compose_open_parameters (GeditDBus *dbus, GeditCommandLine *command_line)
 	                     screen_number,
 	                     workspace,
 	                     viewport_x,
-	                     viewport_y);
+	                     viewport_y,
+	                     stdin_path);
 
+	g_free (stdin_path);
 	return ret;
 }
 
 static void
-slave_open_ready_cb (GDBusProxy   *proxy,
-                     GAsyncResult *result,
-                     GeditDBus    *dbus)
+slave_open_ready_cb (GDBusConnection *connection,
+                     GAsyncResult    *result,
+                     GeditDBus       *dbus)
 {
-	GVariant *ret;
+	GDBusMessage *ret;
 	GError *error = NULL;
 	GeditCommandLine *command_line;
 
-	ret = g_dbus_proxy_invoke_method_finish (proxy, result, &error);
+	ret = g_dbus_connection_send_message_with_reply_finish (connection,
+	                                                        result,
+	                                                        &error);
 	command_line = gedit_command_line_get_default ();
 
+	/* TODO: start proxying stdin if needed */
+
 	if (ret == NULL)
 	{
 		g_warning ("Failed to call gedit service: %s", error->message);
@@ -268,7 +375,10 @@ slave_open_ready_cb (GDBusProxy   *proxy,
 	}
 	else
 	{
-		g_variant_get (ret, "(u)", &dbus->priv->wait_id);
+		g_variant_get (g_dbus_message_get_body (ret),
+		               "(u)",
+		               &dbus->priv->wait_id);
+
 		dbus->priv->result = GEDIT_DBUS_RESULT_SUCCESS;
 
 		if (!gedit_command_line_get_wait (command_line))
@@ -298,6 +408,153 @@ on_open_proxy_signal (GDBusProxy *proxy,
 	}
 }
 
+#ifdef G_OS_UNIX
+static void
+stdin_write_finish (GOutputStream *stream,
+                    GAsyncResult  *result,
+                    AsyncData     *async)
+{
+	GError *error = NULL;
+	gssize written;
+	GeditDBusPrivate *priv;
+
+	if (g_cancellable_is_cancelled (async->cancellable))
+	{
+		async_data_free (async);
+		return;
+	}
+
+	written = g_output_stream_splice_finish (stream, result, &error);
+	priv = async->dbus->priv;
+
+	g_object_unref (priv->stdin_out_stream);
+	g_object_unref (priv->stdin_in_stream);
+
+	priv->stdin_out_stream = NULL;
+	priv->stdin_in_stream = NULL;
+
+	if (written == -1)
+	{
+		g_warning ("Failed to write stdin: %s", error->message);
+		g_error_free (error);
+	}
+
+	async_data_free (async);
+
+	g_object_unref (priv->stdin_fifo);
+	priv->stdin_fifo = NULL;
+
+	g_object_unref (priv->stdin_cancellable);
+	priv->stdin_cancellable = NULL;
+}
+
+static void
+stdin_pipe_ready_to_write (GeditFifo    *fifo,
+                           GAsyncResult *result,
+                           AsyncData    *async)
+{
+	GeditDBusPrivate *priv;
+	GOutputStream *stream;
+	GError *error = NULL;
+
+	if (g_cancellable_is_cancelled (async->cancellable))
+	{
+		async_data_free (async);
+		return;
+	}
+
+	stream = gedit_fifo_open_write_finish (fifo, result, &error);
+
+	if (stream == NULL)
+	{
+		g_warning ("Could not open fifo for writing: %s", error->message);
+		g_error_free (error);
+
+		/* Can't do that then */
+		async_data_free (async);
+		return;
+	}
+
+	priv = async->dbus->priv;
+
+	priv->stdin_out_stream = stream;
+	priv->stdin_in_stream = g_unix_input_stream_new (STDIN_FILENO, TRUE);
+
+	g_output_stream_splice_async (priv->stdin_out_stream,
+	                              priv->stdin_in_stream,
+	                              G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
+	                              G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+	                              G_PRIORITY_DEFAULT,
+	                              async->cancellable,
+	                              (GAsyncReadyCallback)stdin_write_finish,
+	                              async);
+}
+
+static void
+open_command_add_stdin_pipe (GeditDBus *dbus)
+{
+	AsyncData *async;
+
+	dbus->priv->stdin_fifo = gedit_fifo_new (NULL);
+
+	if (!dbus->priv->stdin_fifo)
+	{
+		g_warning ("Failed to create fifo for standard in");
+		return;
+	}
+
+	async = async_data_new (dbus);
+
+	gedit_fifo_open_write_async (dbus->priv->stdin_fifo,
+	                             G_PRIORITY_DEFAULT,
+	                             async->cancellable,
+	                             (GAsyncReadyCallback)stdin_pipe_ready_to_write,
+	                             async);
+}
+#endif
+
+static void
+open_command_add_stdin (GeditDBus       *dbus,
+                        GDBusConnection *connection,
+                        GDBusMessage    *message)
+{
+#ifdef G_OS_UNIX
+	GUnixFDList *fdlist;
+	GError *error = NULL;
+	gint ret;
+
+	if (!gedit_utils_can_read_from_stdin ())
+	{
+		return;
+	}
+
+	if (!(g_dbus_connection_get_capabilities (connection) &
+	      G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING))
+	{
+		/* Fallback with named pipe */
+		open_command_add_stdin_pipe (dbus);
+		return;
+	}
+
+	fdlist = g_unix_fd_list_new ();
+	ret = g_unix_fd_list_append (fdlist, STDIN_FILENO, &error);
+
+	if (ret == -1)
+	{
+		g_warning ("Could not read from standard in: %s", error->message);
+		g_error_free (error);
+	}
+	else
+	{
+		/* Here we can close STDIN because it's dupped */
+		close (STDIN_FILENO);
+	}
+
+	g_dbus_message_set_unix_fd_list (message, fdlist);
+	g_object_unref (fdlist);
+#endif
+}
+
 static void
 command_line_proxy_appeared (GDBusConnection *connection,
                              const gchar     *name,
@@ -306,6 +563,7 @@ command_line_proxy_appeared (GDBusConnection *connection,
                              GeditDBus       *dbus)
 {
 	GeditCommandLine *command_line;
+	GDBusMessage *message;
 
 	command_line = gedit_command_line_get_default ();
 
@@ -317,14 +575,23 @@ command_line_proxy_appeared (GDBusConnection *connection,
 		                  dbus);
 	}
 
-	g_dbus_proxy_invoke_method (proxy,
-	                            "Open",
-	                            compose_open_parameters (dbus, command_line),
-	                            G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-	                           -1,
-	                           NULL,
-	                           (GAsyncReadyCallback)slave_open_ready_cb,
-	                           dbus);
+	message = g_dbus_message_new_method_call (g_dbus_proxy_get_unique_bus_name (proxy),
+	                                          "/org/gnome/gedit",
+	                                          "org.gnome.gedit.CommandLine",
+	                                          "Open");
+
+	open_command_add_stdin (dbus, connection, message);
+	g_dbus_message_set_body (message, compose_open_parameters (dbus));
+
+	g_dbus_connection_send_message_with_reply (g_dbus_proxy_get_connection (proxy),
+	                                           message,
+	                                           -1,
+	                                           NULL,
+	                                           NULL,
+	                                           (GAsyncReadyCallback)slave_open_ready_cb,
+	                                           dbus);
+
+	g_object_unref (message);
 }
 
 static void
@@ -632,6 +899,233 @@ install_wait_handler (GeditDBus       *dbus,
 	g_object_weak_ref (object, (GWeakNotify)unref_wait_handler, data);
 }
 
+#ifdef G_OS_UNIX
+static GeditTab *
+tab_from_stream (GeditWindow         *window,
+                 GInputStream        *stream,
+                 const GeditEncoding *encoding,
+                 gint                 line_position,
+                 gint                 column_position,
+                 gboolean             jump_to)
+{
+	GList *documents;
+	GeditDocument *doc = NULL;
+	GeditTab *tab = NULL;
+
+	documents = gedit_window_get_documents (window);
+
+	if (documents)
+	{
+		doc = GEDIT_DOCUMENT (documents->data);
+		tab = gedit_tab_get_from_document (doc);
+	}
+
+	if (documents && !documents->next &&
+	    gedit_document_is_untouched (doc) &&
+	    gedit_tab_get_state (tab) == GEDIT_TAB_STATE_NORMAL)
+	{
+		/* open right in that document */
+		GeditDocument *doc = GEDIT_DOCUMENT (documents->data);
+
+		tab = gedit_tab_get_from_document (doc);
+
+		_gedit_tab_load_stream (tab,
+		                        stream,
+		                        encoding,
+		                        line_position,
+		                        column_position);
+	}
+	else
+	{
+		tab = gedit_window_create_tab_from_stream (window,
+		                                           stream,
+		                                           encoding,
+		                                           line_position,
+		                                           column_position,
+		                                           jump_to);
+	}
+
+	g_list_free (documents);
+	return tab;
+}
+#endif
+
+static GSList *
+create_tabs_for_fds (GeditDBus             *dbus,
+                     GDBusMethodInvocation *invocation,
+                     GeditWindow           *window,
+                     const GeditEncoding   *encoding,
+                     gint                   line_position,
+                     gint                   column_position,
+                     gboolean               jump_to)
+{
+#ifdef G_OS_UNIX
+	GDBusMessage *message;
+	GDBusConnection *connection;
+	GUnixFDList *fdlist;
+	GSList *ret = NULL;
+	gint num;
+	gint i;
+
+	connection = g_dbus_method_invocation_get_connection (invocation);
+
+	if (!(g_dbus_connection_get_capabilities (connection) &
+	      G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING))
+	{
+		return NULL;
+	}
+
+	message = g_dbus_method_invocation_get_message (invocation);
+	fdlist = g_dbus_message_get_unix_fd_list (message);
+
+	if (!fdlist)
+	{
+		return NULL;
+	}
+
+	num = g_unix_fd_list_get_length (fdlist);
+
+	for (i = 0; i < num; ++i)
+	{
+		gint fd;
+		GError *error = NULL;
+
+		fd = g_unix_fd_list_get (fdlist, i, &error);
+
+		if (fd == -1)
+		{
+			g_warning ("Could not open stream for service: %s", error->message);
+			g_error_free (error);
+			error = NULL;
+		}
+		else
+		{
+			GeditTab *tab;
+			GInputStream *stream;
+
+			/* fd is dupped, so we close it when the stream closes */
+			stream = g_unix_input_stream_new (fd, TRUE);
+
+			tab = tab_from_stream (window,
+			                       stream,
+			                       encoding,
+			                       line_position,
+			                       column_position,
+			                       jump_to);
+
+			g_object_unref (stream);
+
+			if (tab)
+			{
+				ret = g_slist_prepend (ret, tab);
+				jump_to = FALSE;
+			}
+		}
+	}
+
+	return g_slist_reverse (ret);
+#else
+	return NULL;
+#endif
+}
+
+#ifdef G_OS_UNIX
+static void
+stdin_pipe_ready_to_read (GeditFifo    *fifo,
+                          GAsyncResult *result,
+                          AsyncData    *async)
+{
+	GInputStream *stream;
+	GError *error = NULL;
+	GeditTab *tab;
+
+	if (g_cancellable_is_cancelled (async->cancellable))
+	{
+		async_data_free (async);
+		return;
+	}
+
+	stream = gedit_fifo_open_read_finish (fifo, result, &error);
+
+	if (!stream)
+	{
+		g_warning ("Opening stdin pipe error: %s", error->message);
+
+		g_error_free (error);
+
+		g_object_unref (async->dbus->priv->stdin_cancellable);
+		async->dbus->priv->stdin_cancellable = NULL;
+
+		g_object_unref (fifo);
+		async->dbus->priv->stdin_fifo = NULL;
+
+		async_data_free (async);
+		return;
+	}
+
+	tab = tab_from_stream (async->window,
+	                       stream,
+	                       async->encoding,
+	                       async->line_position,
+	                       async->column_position,
+	                       async->jump_to);
+
+	g_object_unref (stream);
+
+	if (async->wait_data)
+	{
+		install_wait_handler (async->dbus,
+		                      async->wait_data,
+		                      G_OBJECT (tab),
+		                      wait_handler_dbus);
+	}
+}
+#endif
+
+static void
+create_tab_for_pipe (GeditDBus           *dbus,
+                     const gchar         *pipe,
+                     GeditWindow         *window,
+                     const GeditEncoding *encoding,
+                     gint                 line_position,
+                     gint                 column_position,
+                     gboolean             jump_to,
+                     WaitData            *wait_data)
+{
+#ifdef G_OS_UNIX
+	/* We'll do this async */
+	GFile *file;
+	AsyncData *async;
+
+	file = g_file_new_for_path (pipe);
+	dbus->priv->stdin_fifo = gedit_fifo_new (file);
+	g_object_unref (file);
+
+	if (dbus->priv->stdin_fifo == NULL)
+	{
+		return;
+	}
+
+	async = async_data_new (dbus);
+	async->window = window;
+	async->encoding = encoding;
+	async->line_position = line_position;
+	async->column_position = column_position;
+	async->jump_to = jump_to;
+	async->wait_data = wait_data;
+
+	g_object_weak_ref (G_OBJECT (window),
+	                   (GWeakNotify)async_window_destroyed,
+	                   async);
+
+	gedit_fifo_open_read_async (dbus->priv->stdin_fifo,
+	                            G_PRIORITY_DEFAULT,
+	                            async->cancellable,
+	                            (GAsyncReadyCallback)stdin_pipe_ready_to_read,
+	                            async);
+#endif
+}
+
 static void
 dbus_handle_open (GeditDBus             *dbus,
                   GVariant              *parameters,
@@ -656,9 +1150,12 @@ dbus_handle_open (GeditDBus             *dbus,
 	GSList *loaded_documents = NULL;
 	gboolean empty_window;
 	WaitData *data;
+	GSList *item;
+	GSList *tabs;
+	gchar *stdin_pipe;
 
 	g_variant_get (parameters,
-	               "(assiibbbusiiii)",
+	               "(assiibbbusiiiis)",
 	               &file_list,
 	               &charset_encoding,
 	               &line_position,
@@ -671,7 +1168,8 @@ dbus_handle_open (GeditDBus             *dbus,
 	               &screen_number,
 	               &workspace,
 	               &viewport_x,
-	               &viewport_y);
+	               &viewport_y,
+	               &stdin_pipe);
 
 	locations = variant_iter_list_to_locations (file_list);
 	g_variant_iter_free (file_list);
@@ -702,20 +1200,46 @@ dbus_handle_open (GeditDBus             *dbus,
 		                                                      column_position);
 	}
 
-	if (new_document)
-	{
-		GeditTab *tab;
-		tab = gedit_window_create_tab (window, TRUE);
+	g_slist_free (locations);
+
+	tabs = create_tabs_for_fds (dbus,
+	                            invocation,
+	                            window,
+	                            encoding,
+	                            line_position,
+	                            column_position,
+	                            loaded_documents == NULL);
 
-		loaded_documents = g_slist_append (loaded_documents, gedit_tab_get_document (tab));
+	for (item = tabs; item; item = g_slist_next (item))
+	{
+		loaded_documents = g_slist_append (loaded_documents, gedit_tab_get_document (item->data));
 	}
 
-	set_interaction_time_and_present (window, startup_time);
+	g_slist_free (tabs);
 
-	g_slist_free (locations);
+	set_interaction_time_and_present (window, startup_time);
 
 	if (!wait)
 	{
+		if (stdin_pipe && *stdin_pipe)
+		{
+			create_tab_for_pipe (dbus,
+			                     stdin_pipe,
+			                     window,
+			                     encoding,
+			                     line_position,
+			                     column_position,
+			                     loaded_documents == NULL && !new_document,
+			                     NULL);
+		}
+
+		g_free (stdin_pipe);
+
+		if (new_document)
+		{
+			gedit_window_create_tab (window, loaded_documents == NULL);
+		}
+
 		g_slist_free (loaded_documents);
 		g_dbus_method_invocation_return_value (invocation,
 		                                       g_variant_new ("(u)", 0));
@@ -729,8 +1253,30 @@ dbus_handle_open (GeditDBus             *dbus,
 	data->close_window = empty_window;
 	data->wait_id = ++dbus->priv->next_wait_id;
 
-	if (loaded_documents == NULL)
+	if (stdin_pipe && *stdin_pipe)
+	{
+		create_tab_for_pipe (dbus,
+		                     stdin_pipe,
+		                     window,
+		                     encoding,
+		                     line_position,
+		                     column_position,
+		                     loaded_documents == NULL && !new_document,
+		                     data);
+	}
+
+	if (new_document)
 	{
+		GeditTab *tab;
+		tab = gedit_window_create_tab (window, loaded_documents == NULL);
+
+		loaded_documents = g_slist_append (loaded_documents,
+		                                   gedit_tab_get_document (tab));
+	}
+
+	if (loaded_documents == NULL && !(stdin_pipe && *stdin_pipe))
+	{
+		/* Add wait handler on the window */
 		install_wait_handler (dbus,
 		                      data,
 		                      G_OBJECT (window),
@@ -750,6 +1296,8 @@ dbus_handle_open (GeditDBus             *dbus,
 		}
 	}
 
+	g_free (stdin_pipe);
+
 	g_slist_free (loaded_documents);
 	g_dbus_method_invocation_return_value (invocation, g_variant_new ("(u)", data->wait_id));
 }
@@ -792,6 +1340,7 @@ static const GDBusArgInfo in_args[] = {
 	{"workspace"      , "i"},
 	{"viewport_x"     , "i"},
 	{"viewport_y"     , "i"},
+	{"stdin_pipe"     , "s"}
 };
 
 static const GDBusArgInfo out_args[] = {
@@ -799,7 +1348,7 @@ static const GDBusArgInfo out_args[] = {
 };
 
 static const GDBusMethodInfo command_line_methods[] = {
-	{"Open", "assiibbbusiiii", 13, in_args, "u", 1, out_args}
+	{"Open", "assiibbbusiiiis", 14, in_args, "u", 1, out_args}
 };
 
 static const GDBusArgInfo signal_args[] = {
diff --git a/gedit/gedit-document-loader.c b/gedit/gedit-document-loader.c
index a1dd214..2de4c63 100644
--- a/gedit/gedit-document-loader.c
+++ b/gedit/gedit-document-loader.c
@@ -567,6 +567,24 @@ async_read_cb (GInputStream *stream,
 		return;
 	}
 
+	/* When reading just from the stream, try to do content-type
+	   detection based on data */
+	if (loader->priv->location == NULL && loader->priv->info == NULL)
+	{
+		gchar *guessed;
+
+		loader->priv->info = g_file_info_new ();
+
+		guessed = g_content_type_guess (NULL,
+		                                (guchar *)loader->priv->buffer,
+		                                async->read,
+		                                NULL);
+
+		g_file_info_set_attribute_string (loader->priv->info,
+		                                  G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+		                                  guessed);
+	}
+
 	/* Bump the size. */
 	loader->priv->bytes_read += async->read;
 
diff --git a/gedit/gedit-fifo.c b/gedit/gedit-fifo.c
new file mode 100644
index 0000000..79afaf5
--- /dev/null
+++ b/gedit/gedit-fifo.c
@@ -0,0 +1,347 @@
+#include "gedit-fifo.h"
+#include <stdio.h>
+#include <gio/gunixinputstream.h>
+#include <gio/gunixoutputstream.h>
+#include <errno.h>
+#include <string.h>
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#define GEDIT_FIFO_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GEDIT_TYPE_FIFO, GeditFifoPrivate))
+
+/* Properties */
+enum
+{
+	PROP_0,
+	PROP_FILE
+};
+
+struct _GeditFifoPrivate
+{
+	GFile *file;
+	gint open_mode;
+};
+
+static void gedit_fifo_initable_iface_init (gpointer giface, gpointer iface_data);
+
+G_DEFINE_TYPE_WITH_CODE (GeditFifo,
+                         gedit_fifo,
+                         G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gedit_fifo_initable_iface_init))
+
+static gboolean
+gedit_fifo_initable_init (GInitable     *initable,
+                          GCancellable  *cancellable,
+                          GError       **error)
+{
+	g_return_val_if_fail (GEDIT_IS_FIFO (initable), FALSE);
+
+	if (cancellable && g_cancellable_set_error_if_cancelled (cancellable, error))
+	{
+		return FALSE;
+	}
+
+	return GEDIT_FIFO (initable)->priv->file != NULL;
+}
+
+static void
+gedit_fifo_initable_iface_init (gpointer giface, gpointer iface_data)
+{
+	GInitableIface *iface = giface;
+
+	iface->init = gedit_fifo_initable_init;
+}
+
+static void
+gedit_fifo_finalize (GObject *object)
+{
+	GeditFifo *self = GEDIT_FIFO (object);
+
+	if (self->priv->file)
+	{
+		g_object_unref (self->priv->file);
+	}
+
+	G_OBJECT_CLASS (gedit_fifo_parent_class)->finalize (object);
+}
+
+static void
+gedit_fifo_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+	GeditFifo *self = GEDIT_FIFO (object);
+	
+	switch (prop_id)
+	{
+		case PROP_FILE:
+			self->priv->file = g_value_dup_object (value);
+		break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gedit_fifo_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+	GeditFifo *self = GEDIT_FIFO (object);
+	
+	switch (prop_id)
+	{
+		case PROP_FILE:
+			g_value_set_object (value, self->priv->file);
+		break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+init_fifo (GeditFifo *fifo)
+{
+	gchar tmp[] = "gedit-fifo.XXXXXX";
+	gint fd;
+
+	fd = g_mkstemp (tmp);
+
+	if (fd == -1)
+	{
+		g_warning ("Could not generate temporary name for fifo: %s",
+		           strerror (errno));
+		return;
+	}
+
+	close (fd);
+
+	if (g_unlink (tmp) == -1)
+	{
+		return;
+	}
+
+	if (mkfifo (tmp, 0600) == -1)
+	{
+		g_warning ("Could not create named pipe for standard in: %s",
+		           strerror (errno));
+		return;
+	}
+
+	fifo->priv->file = g_file_new_for_path (tmp);
+}
+
+static void
+gedit_fifo_constructed (GObject *object)
+{
+	GeditFifo *self = GEDIT_FIFO (object);
+
+	if (!self->priv->file)
+	{
+		init_fifo (self);
+	}
+	else if (!g_file_query_exists (self->priv->file, NULL))
+	{
+		g_object_unref (self->priv->file);
+		self->priv->file = NULL;
+	}
+}
+
+static void
+gedit_fifo_class_init (GeditFifoClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize = gedit_fifo_finalize;
+
+	object_class->set_property = gedit_fifo_set_property;
+	object_class->get_property = gedit_fifo_get_property;
+
+	object_class->constructed = gedit_fifo_constructed;
+
+	g_object_class_install_property (object_class, PROP_FILE,
+				 g_param_spec_object ("file",
+						      "FILE",
+						      "The fifo file",
+						      G_TYPE_FILE,
+						      G_PARAM_READWRITE |
+						      G_PARAM_CONSTRUCT_ONLY |
+						      G_PARAM_STATIC_STRINGS));
+
+	g_type_class_add_private (object_class, sizeof(GeditFifoPrivate));
+}
+
+static void
+gedit_fifo_init (GeditFifo *self)
+{
+	self->priv = GEDIT_FIFO_GET_PRIVATE (self);
+}
+
+GeditFifo *
+gedit_fifo_new (GFile *file)
+{
+	return g_initable_new (GEDIT_TYPE_FIFO, NULL, NULL, "file", file, NULL);
+}
+
+static void
+fifo_open_in_thread (GSimpleAsyncResult *res,
+                     GObject            *object,
+                     GCancellable       *cancellable)
+{
+	GError *error = NULL;
+	gchar *path;
+	gint fd;
+	GeditFifo *fifo;
+	gpointer stream;
+
+	if (cancellable && g_cancellable_set_error_if_cancelled (cancellable, &error))
+	{
+		g_simple_async_result_set_from_error (res, error);
+		g_error_free (error);
+		return;
+	}
+
+	fifo = GEDIT_FIFO (object);
+	path = g_file_get_path (fifo->priv->file);
+	fd = g_open (path, fifo->priv->open_mode, 0);
+	g_free (path);
+
+	if (cancellable && g_cancellable_set_error_if_cancelled (cancellable, &error))
+	{
+		if (fd != -1)
+		{
+			close (fd);
+		}
+
+		g_simple_async_result_set_from_error (res, error);
+		g_error_free (error);
+		return;
+	}
+
+	if (fd == -1)
+	{
+		g_simple_async_result_set_error (res,
+		                                 G_IO_ERROR,
+		                                 g_io_error_from_errno (errno),
+		                                 "%s",
+		                                 strerror (errno));
+		return;
+	}
+
+	if (fifo->priv->open_mode & O_WRONLY)
+	{
+		stream = g_unix_output_stream_new (fd, TRUE);
+	}
+	else
+	{
+		stream = g_unix_input_stream_new (fd, TRUE);
+	}
+
+	g_simple_async_result_set_op_res_gpointer (res,
+	                                           stream,
+	                                           (GDestroyNotify)g_object_unref);
+}
+
+static void
+async_open (GeditFifo           *fifo,
+            gint                 mode,
+            gint                 io_priority,
+            GCancellable        *cancellable,
+            GAsyncReadyCallback  callback,
+            gpointer             user_data)
+{
+	GSimpleAsyncResult *ret;
+
+	fifo->priv->open_mode = mode;
+
+	ret = g_simple_async_result_new (G_OBJECT (fifo),
+	                                 callback,
+	                                 user_data,
+	                                 fifo_open_in_thread);
+
+	g_simple_async_result_run_in_thread (ret,
+	                                     fifo_open_in_thread,
+	                                     io_priority,
+	                                     cancellable);
+}
+
+GInputStream *
+gedit_fifo_open_read_finish (GeditFifo     *fifo,
+                             GAsyncResult  *result,
+                             GError       **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (GEDIT_IS_FIFO (fifo), NULL);
+	g_return_val_if_fail (g_simple_async_result_is_valid (result,
+	                                                      G_OBJECT (fifo),
+	                                                      fifo_open_in_thread),
+	                      NULL);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	if (g_simple_async_result_propagate_error (simple, error))
+	{
+		return NULL;
+	}
+
+	return G_INPUT_STREAM (g_object_ref (g_simple_async_result_get_op_res_gpointer (simple)));
+}
+
+GOutputStream *
+gedit_fifo_open_write_finish (GeditFifo     *fifo,
+                              GAsyncResult  *result,
+                              GError       **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (GEDIT_IS_FIFO (fifo), NULL);
+	g_return_val_if_fail (g_simple_async_result_is_valid (result,
+	                                                      G_OBJECT (fifo),
+	                                                      fifo_open_in_thread),
+	                      NULL);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	if (g_simple_async_result_propagate_error (simple, error))
+	{
+		return NULL;
+	}
+
+	return G_OUTPUT_STREAM (g_simple_async_result_get_op_res_gpointer (simple));
+}
+
+void
+gedit_fifo_open_read_async (GeditFifo           *fifo,
+                            gint                 io_priority,
+                            GCancellable        *cancellable,
+                            GAsyncReadyCallback  callback,
+                            gpointer             user_data)
+{
+	g_return_if_fail (GEDIT_IS_FIFO (fifo));
+
+	async_open (fifo, O_RDONLY, io_priority, cancellable, callback, user_data);
+}
+
+void
+gedit_fifo_open_write_async (GeditFifo           *fifo,
+                             gint                 io_priority,
+                             GCancellable        *cancellable,
+                             GAsyncReadyCallback  callback,
+                             gpointer             user_data)
+{
+	g_return_if_fail (GEDIT_IS_FIFO (fifo));
+
+	async_open (fifo, O_WRONLY, io_priority, cancellable, callback, user_data);
+}
+
+GFile *
+gedit_fifo_get_file (GeditFifo *fifo)
+{
+	g_return_val_if_fail (GEDIT_IS_FIFO (fifo), NULL);
+	return g_file_dup (fifo->priv->file);
+}
diff --git a/gedit/gedit-fifo.h b/gedit/gedit-fifo.h
new file mode 100644
index 0000000..c59ddb4
--- /dev/null
+++ b/gedit/gedit-fifo.h
@@ -0,0 +1,58 @@
+#ifndef __GEDIT_FIFO_H__
+#define __GEDIT_FIFO_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GEDIT_TYPE_FIFO				(gedit_fifo_get_type ())
+#define GEDIT_FIFO(obj)				(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_FIFO, GeditFifo))
+#define GEDIT_FIFO_CONST(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_FIFO, GeditFifo const))
+#define GEDIT_FIFO_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_FIFO, GeditFifoClass))
+#define GEDIT_IS_FIFO(obj)			(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_FIFO))
+#define GEDIT_IS_FIFO_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_FIFO))
+#define GEDIT_FIFO_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_FIFO, GeditFifoClass))
+
+typedef struct _GeditFifo			GeditFifo;
+typedef struct _GeditFifoClass		GeditFifoClass;
+typedef struct _GeditFifoPrivate	GeditFifoPrivate;
+
+struct _GeditFifo {
+	GObject parent;
+	
+	GeditFifoPrivate *priv;
+};
+
+struct _GeditFifoClass {
+	GObjectClass parent_class;
+};
+
+GType gedit_fifo_get_type (void) G_GNUC_CONST;
+
+GeditFifo *gedit_fifo_new (GFile *file);
+GFile *gedit_fifo_get_file (GeditFifo *fifo);
+
+void gedit_fifo_open_read_async (GeditFifo           *fifo,
+                                 gint                 io_priority,
+                                 GCancellable        *cancellable,
+                                 GAsyncReadyCallback  callback,
+                                 gpointer             user_data);
+
+void gedit_fifo_open_write_async (GeditFifo           *fifo,
+                                  gint                 io_priority,
+                                  GCancellable        *cancellable,
+                                  GAsyncReadyCallback  callback,
+                                  gpointer             user_data);
+
+GOutputStream *gedit_fifo_open_write_finish (GeditFifo     *fifo,
+                                             GAsyncResult  *result,
+                                             GError       **error);
+
+GInputStream *gedit_fifo_open_read_finish (GeditFifo     *fifo,
+                                           GAsyncResult  *result,
+                                           GError       **error);
+
+G_END_DECLS
+
+#endif /* __GEDIT_FIFO_H__ */
diff --git a/gedit/gedit-utils.c b/gedit/gedit-utils.c
index 4f76e75..071e137 100644
--- a/gedit/gedit-utils.c
+++ b/gedit/gedit-utils.c
@@ -59,6 +59,10 @@
 #include <X11/Xatom.h>
 #endif
 
+#ifdef G_OS_UNIX
+#include <unistd.h>
+#endif
+
 #include "gseal-gtk-compat.h"
 
 #define STDIN_DELAY_MICROSECONDS 100000
@@ -1525,4 +1529,15 @@ gedit_utils_decode_uri (const gchar *uri,
 	
 	return TRUE;
 }
+
+gboolean
+gedit_utils_can_read_from_stdin (void)
+{
+#ifdef G_OS_UNIX
+	return !isatty (STDIN_FILENO);
+#else
+	return FALSE;
+#endif
+}
+
 /* ex:ts=8:noet: */
diff --git a/gedit/gedit-utils.h b/gedit/gedit-utils.h
index 80d24a2..8c8c6d4 100644
--- a/gedit/gedit-utils.h
+++ b/gedit/gedit-utils.h
@@ -154,6 +154,8 @@ gboolean	 gedit_utils_decode_uri 		(const gchar *uri,
 /* Turns data from a drop into a list of well formatted uris */
 gchar 	       **gedit_utils_drop_get_uris		(GtkSelectionData *selection_data);
 
+gboolean	 gedit_utils_can_read_from_stdin	(void);
+
 G_END_DECLS
 
 #endif /* __GEDIT_UTILS_H__ */
diff --git a/gedit/gedit.c b/gedit/gedit.c
index cf55c2f..c6d5c64 100644
--- a/gedit/gedit.c
+++ b/gedit/gedit.c
@@ -59,27 +59,21 @@
 #endif
 
 #ifdef G_OS_UNIX
-#include <unistd.h>
 #include <gio/gunixinputstream.h>
+#include <unistd.h>
 #endif
 
-#ifdef G_OS_UNIX
-static gboolean
+static void
 gedit_main_load_from_stdin (GeditWindow *window,
                             gboolean jump_to)
 {
+#ifdef G_OS_UNIX
 	GInputStream *stream;
 	const GeditEncoding *encoding;
 	gint line_position;
 	gint column_position;
 	GeditCommandLine *command_line;
 
-	/* Only if it's not a tty */
-	if (isatty (STDIN_FILENO))
-	{
-		return FALSE;
-	}
-
 	command_line = gedit_command_line_get_default ();
 
 	encoding = gedit_command_line_get_encoding (command_line);
@@ -96,10 +90,8 @@ gedit_main_load_from_stdin (GeditWindow *window,
 	                                     column_position,
 	                                     jump_to);
 	g_object_unref (stream);
-
-	return TRUE;
-}
 #endif
+}
 
 static void
 gedit_main_window (void)
@@ -140,13 +132,11 @@ gedit_main_window (void)
 		g_slist_free (loaded);
 	}
 
-#ifdef G_OS_UNIX
-	/* We can only do this on unix systems */
-	if (gedit_main_load_from_stdin (window, !doc_created))
+	if (gedit_utils_can_read_from_stdin ())
 	{
+		gedit_main_load_from_stdin (window, !doc_created);
 		doc_created = TRUE;
 	}
-#endif
 
 	if (!doc_created || gedit_command_line_get_new_document (command_line))
 	{
@@ -203,6 +193,7 @@ gedit_main (gboolean service)
 
 	/* Cleanup */
 	g_object_unref (engine);
+	g_object_unref (app);
 
 #ifndef ENABLE_GVFS_METADATA
 	gedit_metadata_manager_shutdown ();



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