SF.net SVN: geany: [2348] trunk

eht16 at users.sourceforge.net eht16 at xxxxx
Fri Mar 14 18:55:31 UTC 2008


Revision: 2348
          http://geany.svn.sourceforge.net/geany/?rev=2348&view=rev
Author:   eht16
Date:     2008-03-14 11:55:30 -0700 (Fri, 14 Mar 2008)

Log Message:
-----------
Fix freeze when reading from stderr when using "Send Selection To".
Don't modify current selection if the executed command returned error messages and/or an unsuccessful exit code.
Improve error reporting when using "Send Selection To" (#1909452).

Modified Paths:
--------------
    trunk/ChangeLog
    trunk/src/tools.c

Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog	2008-03-14 17:44:43 UTC (rev 2347)
+++ trunk/ChangeLog	2008-03-14 18:55:30 UTC (rev 2348)
@@ -25,6 +25,11 @@
    command on Windows (code moved from build.c).
  * geany.spec.in: Add pkg-config file and new include directory to the
                   files section (thanks to Dominic Hopf).
+ * src/tools.c:
+   Fix freeze when reading from stderr when using "Send Selection To".
+   Don't modify current selection if the executed command returned error
+   messages and/or an unsuccessful exit code.
+   Improve error reporting when using "Send Selection To" (#1909452).
 
 
 2008-03-13  Nick Treleaven  <nick(dot)treleaven(at)btinternet(dot)com>

Modified: trunk/src/tools.c
===================================================================
--- trunk/src/tools.c	2008-03-14 17:44:43 UTC (rev 2347)
+++ trunk/src/tools.c	2008-03-14 18:55:30 UTC (rev 2348)
@@ -49,6 +49,7 @@
 #include "keybindings.h"
 #include "templates.h"
 #include "win32.h"
+#include "dialogs.h"
 
 
 /* custom commands code*/
@@ -58,6 +59,9 @@
 	GtkWidget *box;
 };
 
+static gboolean cc_error_occurred = FALSE;
+static gboolean cc_reading_finished = FALSE;
+static GString *cc_buffer;
 
 static void cc_add_command(struct cc_dialog *cc, gint idx)
 {
@@ -91,18 +95,18 @@
 {
 	if (cond & (G_IO_IN | G_IO_PRI))
 	{
-		gint idx = GPOINTER_TO_INT(data);
 		gchar *msg = NULL;
-		GString *str = g_string_sized_new(256);
 		GIOStatus rv;
 		GError *err = NULL;
 
+		cc_buffer = g_string_sized_new(256);
+
 		do
 		{
 			rv = g_io_channel_read_line(ioc, &msg, NULL, NULL, &err);
 			if (msg != NULL)
 			{
-				g_string_append(str, msg);
+				g_string_append(cc_buffer, msg);
 				g_free(msg);
 			}
 			if (err != NULL)
@@ -113,15 +117,10 @@
 			}
 		} while (rv == G_IO_STATUS_NORMAL || rv == G_IO_STATUS_AGAIN);
 
-		if (rv == G_IO_STATUS_EOF)
-		{	/* Command completed successfully */
-			sci_replace_sel(doc_list[idx].sci, str->str);
-		}
-		else
+		if (rv != G_IO_STATUS_EOF)
 		{	/* Something went wrong? */
 			g_warning("%s: %s\n", __func__, "Incomplete command output");
 		}
-		g_string_free(str, TRUE);
 	}
 	return FALSE;
 }
@@ -132,19 +131,98 @@
 	if (cond & (G_IO_IN | G_IO_PRI))
 	{
 		gchar *msg = NULL;
+		GString *str = g_string_sized_new(256);
+		GIOStatus rv;
 
-		while (g_io_channel_read_line(ioc, &msg, NULL, NULL, NULL) && msg != NULL)
+		do
 		{
-			g_warning("%s: %s", (const gchar*) data, g_strstrip(msg));
-			g_free(msg);
+			rv = g_io_channel_read_line(ioc, &msg, NULL, NULL, NULL);
+			if (msg != NULL)
+			{
+				g_string_append(str, msg);
+				g_free(msg);
+			}
+		} while (rv == G_IO_STATUS_NORMAL || rv == G_IO_STATUS_AGAIN);
+
+		if (NZV(str->str))
+		{
+			g_warning("%s: %s\n", (const gchar *) data, str->str);
+			ui_set_statusbar(TRUE,
+				_("The executed custom command returned an error. "
+				"Your selection was not changed. Error message: %s"),
+				str->str);
+			cc_error_occurred = TRUE;
+
 		}
+		g_string_free(str, TRUE);
+	}
+	cc_reading_finished = TRUE;
+	return FALSE;
+}
+
+
+static gboolean cc_replace_sel_cb(gpointer user_data)
+{
+	gint idx = GPOINTER_TO_INT(user_data);
+
+	if (! cc_reading_finished)
+	{	/* keep this function in the main loop until cc_iofunc_err() has finished */
 		return TRUE;
 	}
 
+	if (! cc_error_occurred && cc_buffer != NULL)
+	{	/* Command completed successfully */
+		sci_replace_sel(doc_list[idx].sci, cc_buffer->str);
+		g_string_free(cc_buffer, TRUE);
+		cc_buffer = NULL;
+	}
+
+	cc_error_occurred = FALSE;
+	cc_reading_finished = FALSE;
+
 	return FALSE;
 }
 
 
+/* check whether the executed command failed and if so do nothing.
+ * If it returned with a sucessful exit code, replace the selection. */
+static void cc_exit_cb(GPid child_pid, gint status, gpointer user_data)
+{
+	/* if there was already an error, skip further checks */
+	if (! cc_error_occurred)
+	{
+#ifdef G_OS_UNIX
+		if (WIFEXITED(status))
+		{
+			if (WEXITSTATUS(status) != EXIT_SUCCESS)
+				cc_error_occurred = TRUE;
+		}
+		else if (WIFSIGNALED(status))
+		{	/* the terminating signal: WTERMSIG (status)); */
+			cc_error_occurred = TRUE;
+		}
+		else
+		{	/* any other failure occured */
+			cc_error_occurred = TRUE;
+		}
+#else
+		cc_error_occurred = ! win32_get_exit_status(child_pid);
+#endif
+
+		if (cc_error_occurred)
+		{	/* here we are sure cc_error_occurred was set due to an unsuccessful exit code
+			 * and so we add an error message */
+			/* TODO maybe include the exit code in the error message */
+			ui_set_statusbar(TRUE,
+				_("The executed custom command exited with an unsuccessful exit code."));
+		}
+	}
+
+	g_idle_add(cc_replace_sel_cb, user_data);
+	g_spawn_close_pid(child_pid);
+}
+
+
 /* Executes command (which should include all necessary command line args) and passes the current
  * selection through the standard input of command. The whole output of command replaces the
  * current selection. */
@@ -165,15 +243,20 @@
 	argv = g_strsplit(command, " ", -1);
 	ui_set_statusbar(TRUE, _("Passing data and executing custom command: %s"), command);
 
-	if (g_spawn_async_with_pipes(NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
+	cc_error_occurred = FALSE;
+
+	if (g_spawn_async_with_pipes(NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
 						NULL, NULL, &pid, &stdin_fd, &stdout_fd, &stderr_fd, &error))
 	{
 		gchar *sel;
 		gint len, remaining, wrote;
 
+		if (pid > 0)
+			g_child_watch_add(pid, (GChildWatchFunc) cc_exit_cb, GINT_TO_POINTER(idx));
+
 		/* use GIOChannel to monitor stdout */
 		utils_set_up_io_channel(stdout_fd, G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
-				FALSE, cc_iofunc, GINT_TO_POINTER(idx));
+				FALSE, cc_iofunc, NULL);
 		/* copy program's stderr to Geany's stdout to help error tracking */
 		utils_set_up_io_channel(stderr_fd, G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
 				FALSE, cc_iofunc_err, (gpointer)command);
@@ -202,6 +285,7 @@
 	else
 	{
 		geany_debug("g_spawn_async_with_pipes() failed: %s", error->message);
+		ui_set_statusbar(TRUE, _("Custom command failed: %s"), error->message);
 		g_error_free(error);
 	}
 


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.



More information about the Commits mailing list