[geany/geany] 7b5df8: Create geany_run_script.sh in the temporary directory instead of the working directory

Jiří Techet git-noreply at xxxxx
Wed Mar 4 10:48:53 UTC 2015


Branch:      refs/heads/master
Author:      Jiří Techet <techet at gmail.com>
Committer:   Jiří Techet <techet at gmail.com>
Date:        Wed, 04 Mar 2015 10:48:53 UTC
Commit:      7b5df86bd14dac77e1d66ede4b8240cd99e8ba48
             https://github.com/geany/geany/commit/7b5df86bd14dac77e1d66ede4b8240cd99e8ba48

Log Message:
-----------
Create geany_run_script.sh in the temporary directory instead of the working directory

Under some conditions, geany_run_script.sh is not deleted and we
have no means to detect this in Geany (e.g. when the terminal emulator
is started correctly but it fails to execute the script for some reason).
In this case it is better to keep the garbage in /tmp than the working
directory. Apart from that, it eliminates potential transfer of the run script
over a NFS and eliminates the visibility of the script in working directory
on Windows.

Apart from that this patch fixes some locale/utf8 conversion problems
and other subtle problems with the previous implementation.


Modified Paths:
--------------
    doc/geany.txt
    src/build.c

Modified: doc/geany.txt
5 lines changed, 3 insertions(+), 2 deletions(-)
===================================================================
@@ -3018,8 +3018,9 @@ The Terminal field of the tools preferences tab requires a command to
 execute the terminal program and to pass it the name of the Geany run
 script that it should execute in a Bourne compatible shell (eg /bin/sh).
 The marker "%c" is substituted with the name of the Geany run script,
-which is created in the working directory set in the Build commands
-dialog, see `Build menu commands dialog`_ for details.
+which is created in the temporary directory and which changes the working
+directory to the directory set in the Build commands dialog, see 
+`Build menu commands dialog`_ for details.
 
 As an example the default (Linux) command is::
 


Modified: src/build.c
167 lines changed, 71 insertions(+), 96 deletions(-)
===================================================================
@@ -91,9 +91,9 @@ typedef struct RunInfo
 static RunInfo *run_info;
 
 #ifdef G_OS_WIN32
-static const gchar RUN_SCRIPT_CMD[] = "geany_run_script.bat";
+static const gchar RUN_SCRIPT_CMD[] = "geany_run_script_XXXXXX.bat";
 #else
-static const gchar RUN_SCRIPT_CMD[] = "./geany_run_script.sh";
+static const gchar RUN_SCRIPT_CMD[] = "geany_run_script_XXXXXX.sh";
 #endif
 
 /* pack group (<8) and command (<32) into a user_data pointer */
@@ -128,7 +128,7 @@ static guint build_items_count = 9;
 static void build_exit_cb(GPid child_pid, gint status, gpointer user_data);
 static gboolean build_iofunc(GIOChannel *ioc, GIOCondition cond, gpointer data);
 #endif
-static gboolean build_create_shellscript(const gchar *fname, const gchar *working_dir, const gchar *cmd, gboolean autoclose, GError **error);
+static gchar *build_create_shellscript(const gchar *working_dir, const gchar *cmd, gboolean autoclose, GError **error);
 static GPid build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir);
 static void set_stop_button(gboolean stop);
 static void run_exit_cb(GPid child_pid, gint status, gpointer user_data);
@@ -879,54 +879,49 @@ static GPid build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *d
 }
 
 
-/* Returns: NULL if there was an error, or the working directory the script was created in.
- * vte_cmd_nonscript is the location of a string which is filled with the command to be used
- * when vc->skip_run_script is set, otherwise it will be set to NULL */
-static gchar *prepare_run_script(GeanyDocument *doc, gchar **vte_cmd_nonscript, guint cmdindex)
+/* Returns: NULL if there was an error, or the command to be executed. If Geany is
+ * set to use a run script, the returned value is a path to the script that runs
+ * the command; otherwise the command itself is returned. working_dir is a pointer
+ * to the working directory from which the command is executed. Both strings are
+ * in the locale encoding. */
+static gchar *prepare_run_cmd(GeanyDocument *doc, gchar **working_dir, guint cmdindex)
 {
 	GeanyBuildCommand *cmd = NULL;
-	gchar *working_dir = NULL;
 	const gchar *cmd_working_dir;
 	gboolean autoclose = FALSE;
-	gboolean result = FALSE;
-	gchar *tmp;
-	gchar *cmd_string;
+	gchar *cmd_string_utf8, *working_dir_utf8, *run_cmd, *cmd_string;
 	GError *error = NULL;
 
-	if (vte_cmd_nonscript != NULL)
-		*vte_cmd_nonscript = NULL;
-
 	cmd = get_build_cmd(doc, GEANY_GBG_EXEC, cmdindex, NULL);
 
-	cmd_string = build_replace_placeholder(doc, cmd->command);
+	cmd_string_utf8 = build_replace_placeholder(doc, cmd->command);
 	cmd_working_dir =  cmd->working_dir;
 	if (EMPTY(cmd_working_dir))
 		cmd_working_dir = "%d";
-	working_dir = build_replace_placeholder(doc, cmd_working_dir); /* in utf-8 */
+	working_dir_utf8 = build_replace_placeholder(doc, cmd_working_dir);
+	*working_dir = utils_get_locale_from_utf8(working_dir_utf8);
 
 	/* only test whether working dir exists, don't change it or else Windows support will break
 	 * (gspawn-win32-helper.exe is used by GLib and must be in $PATH which means current working
 	 *  dir where geany.exe was started from, so we can't change it) */
-	if (EMPTY(working_dir) || ! g_file_test(working_dir, G_FILE_TEST_EXISTS) ||
-		! g_file_test(working_dir, G_FILE_TEST_IS_DIR))
+	if (EMPTY(*working_dir) || ! g_file_test(*working_dir, G_FILE_TEST_EXISTS) ||
+		! g_file_test(*working_dir, G_FILE_TEST_IS_DIR))
 	{
 		ui_set_statusbar(TRUE, _("Failed to change the working directory to \"%s\""),
-				!EMPTY(working_dir) ? working_dir : "<NULL>" );
-		utils_free_pointers(2, cmd_string, working_dir, NULL);
+				!EMPTY(working_dir_utf8) ? working_dir_utf8 : "<NULL>" );
+		utils_free_pointers(3, cmd_string_utf8, working_dir_utf8, *working_dir, NULL);
 		return NULL;
 	}
 
+	cmd_string = utils_get_locale_from_utf8(cmd_string_utf8);
+
 #ifdef HAVE_VTE
 	if (vte_info.have_vte && vc->run_in_vte)
 	{
 		if (vc->skip_run_script)
 		{
-			if (vte_cmd_nonscript != NULL)
-				*vte_cmd_nonscript = cmd_string;
-			else
-				g_free(cmd_string);
-
-			return working_dir;
+			utils_free_pointers(2, cmd_string_utf8, working_dir_utf8, NULL);
+			return cmd_string;
 		}
 		else
 			/* don't wait for user input at the end of script when we are running in VTE */
@@ -934,38 +929,31 @@ static gchar *prepare_run_script(GeanyDocument *doc, gchar **vte_cmd_nonscript,
 	}
 #endif
 
-	/* RUN_SCRIPT_CMD should be ok in UTF8 without converting in locale because it
-	 * contains no umlauts */
-	tmp = g_build_filename(working_dir, RUN_SCRIPT_CMD, NULL);
-	result = build_create_shellscript(tmp, working_dir, cmd_string, autoclose, &error);
-	if (! result)
+	run_cmd = build_create_shellscript(*working_dir, cmd_string, autoclose, &error);
+	if (!run_cmd)
 	{
 		ui_set_statusbar(TRUE, _("Failed to execute \"%s\" (start-script could not be created: %s)"),
-			!EMPTY(cmd_string) ? cmd_string : NULL, error->message);
+			!EMPTY(cmd_string_utf8) ? cmd_string_utf8 : NULL, error->message);
 		g_error_free(error);
+		g_free(*working_dir);
 	}
 
-	utils_free_pointers(2, cmd_string, tmp, NULL);
-
-	if (result)
-		return working_dir;
-
-	g_free(working_dir);
-	return NULL;
+	utils_free_pointers(3, cmd_string_utf8, working_dir_utf8, cmd_string, NULL);
+	return run_cmd;
 }
 
 
 static GPid build_run_cmd(GeanyDocument *doc, guint cmdindex)
 {
 	gchar *working_dir;
-	gchar *vte_cmd_nonscript = NULL;
+	gchar *run_cmd = NULL;
 	GError *error = NULL;
 
 	if (! DOC_VALID(doc) || doc->file_name == NULL)
 		return (GPid) 0;
 
-	working_dir = prepare_run_script(doc, &vte_cmd_nonscript, cmdindex);
-	if (working_dir == NULL)
+	run_cmd = prepare_run_cmd(doc, &working_dir, cmdindex);
+	if (run_cmd == NULL)
 		return (GPid) 0;
 
 	run_info[cmdindex].file_type_id = doc->file_type->id;
@@ -975,28 +963,23 @@ static GPid build_run_cmd(GeanyDocument *doc, guint cmdindex)
 	{
 		gchar *vte_cmd;
 
+		/* VTE expects commands in UTF-8 */
+		SETPTR(run_cmd, utils_get_utf8_from_locale(run_cmd));
+		SETPTR(working_dir, utils_get_utf8_from_locale(working_dir));
+
 		if (vc->skip_run_script)
-		{
-			SETPTR(vte_cmd_nonscript, utils_get_utf8_from_locale(vte_cmd_nonscript));
-			vte_cmd = g_strconcat(vte_cmd_nonscript, "\n", NULL);
-			g_free(vte_cmd_nonscript);
-		}
+			vte_cmd = g_strconcat(run_cmd, "\n", NULL);
 		else
-			vte_cmd = g_strconcat("\n/bin/sh ", RUN_SCRIPT_CMD, "\n", NULL);
+			vte_cmd = g_strconcat("\n/bin/sh ", run_cmd, "\n", NULL);
 
-		/* change into current directory if it is not done by default */
-		if (! vc->follow_path)
-		{
-			/* we need to convert the working_dir back to UTF-8 because the VTE expects it */
-			gchar *utf8_working_dir = utils_get_utf8_from_locale(working_dir);
-			vte_cwd(utf8_working_dir, TRUE);
-			g_free(utf8_working_dir);
-		}
+		vte_cwd(working_dir, TRUE);
 		if (! vte_send_cmd(vte_cmd))
 		{
 			ui_set_statusbar(FALSE,
 		_("Could not execute the file in the VTE because it probably contains a command."));
 			geany_debug("Could not execute the file in the VTE because it probably contains a command.");
+			if (!vc->skip_run_script)
+				g_unlink(run_cmd);
 		}
 
 		/* show the VTE */
@@ -1025,8 +1008,7 @@ static GPid build_run_cmd(GeanyDocument *doc, guint cmdindex)
 				_("Could not parse terminal command \"%s\" "
 					"(check Terminal tool setting in Preferences)"), tool_prefs.term_cmd);
 			run_info[cmdindex].pid = (GPid) 1;
-			script_path = g_build_filename(working_dir, RUN_SCRIPT_CMD, NULL);
-			g_unlink(script_path);
+			g_unlink(run_cmd);
 			goto free_strings;
 		}
 
@@ -1044,14 +1026,13 @@ static GPid build_run_cmd(GeanyDocument *doc, guint cmdindex)
 				_("Could not find terminal \"%s\" "
 					"(check path for Terminal tool setting in Preferences)"), tool_prefs.term_cmd);
 			run_info[cmdindex].pid = (GPid) 1;
-			script_path = g_build_filename(working_dir, RUN_SCRIPT_CMD, NULL);
-			g_unlink(script_path);
+			g_unlink(run_cmd);
 			goto free_strings;
 		}
 
 		for (i = 0; i < argv_len; i++)
 		{
-			utils_str_replace_all(&(argv[i]), "%c", RUN_SCRIPT_CMD);
+			utils_str_replace_all(&(argv[i]), "%c", run_cmd);
 		}
 
 		if (! g_spawn_async(working_dir, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
@@ -1060,8 +1041,7 @@ static GPid build_run_cmd(GeanyDocument *doc, guint cmdindex)
 			geany_debug("g_spawn_async() failed: %s", error->message);
 			ui_set_statusbar(TRUE, _("Process failed (%s)"), error->message);
 			g_error_free(error);
-			script_path = g_build_filename(working_dir, RUN_SCRIPT_CMD, NULL);
-			g_unlink(script_path);
+			g_unlink(run_cmd);
 			error = NULL;
 			run_info[cmdindex].pid = (GPid) 0;
 		}
@@ -1079,6 +1059,7 @@ static GPid build_run_cmd(GeanyDocument *doc, guint cmdindex)
 	}
 
 	g_free(working_dir);
+	g_free(run_cmd);
 	return run_info[cmdindex].pid;
 }
 
@@ -1270,36 +1251,28 @@ static void run_exit_cb(GPid child_pid, gint status, gpointer user_data)
 }
 
 
-static void set_file_error_from_errno(GError **error, gint err, const gchar *prefix)
-{
-	g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(err), "%s%s%s",
-		prefix ? prefix : "", prefix ? ": " : "", g_strerror(err));
-}
-
-
 /* write a little shellscript to call the executable (similar to anjuta_launcher but "internal")
- * fname is the full file name (including path) for the script to create */
-static gboolean build_create_shellscript(const gchar *fname, const gchar *working_dir, const gchar *cmd, gboolean autoclose, GError **error)
+ * working_dir and cmd are both in the locale encoding
+ * it returns the full file name (including path) of the created script in the locale encoding */
+static gchar *build_create_shellscript(const gchar *working_dir, const gchar *cmd, gboolean autoclose, GError **error)
 {
-	FILE *fp;
-	gchar *str;
+	gint fd;
+	gchar *str, *fname;
 	gboolean success = TRUE;
 #ifdef G_OS_WIN32
 	gchar *expanded_cmd;
 #else
 	gchar *escaped_dir;
 #endif
+	fd = g_file_open_tmp (RUN_SCRIPT_CMD, &fname, error);
+	if (fd < 0)
+		return NULL;
+	close(fd);
 
-	fp = g_fopen(fname, "w");
-	if (! fp)
-	{
-		set_file_error_from_errno(error, errno, "Failed to create file");
-		return FALSE;
-	}
 #ifdef G_OS_WIN32
 	/* Expand environment variables like %blah%. */
 	expanded_cmd = win32_expand_environment_variables(cmd);
-	str = g_strdup_printf("%s\n\n%s\ndel \"%%0\"\n\npause\n", expanded_cmd, (autoclose) ? "" : "pause");
+	str = g_strdup_printf("cd \"%s\"\n\n%s\n\n%s\ndel \"%%0\"\n\npause\n", working_dir, expanded_cmd, (autoclose) ? "" : "pause");
 	g_free(expanded_cmd);
 #else
 	escaped_dir = g_strescape(working_dir, NULL);
@@ -1311,29 +1284,31 @@ static gboolean build_create_shellscript(const gchar *fname, const gchar *workin
 	g_free(escaped_dir);
 #endif
 
-	if (fputs(str, fp) < 0)
-	{
-		set_file_error_from_errno(error, errno, "Failed to write file");
+	if (!g_file_set_contents(fname, str, -1, error))
 		success = FALSE;
-	}
 	g_free(str);
-
-	if (fclose(fp) != 0)
-	{
-		if (error && ! *error) /* don't set error twice */
-			set_file_error_from_errno(error, errno, "Failed to close file");
-		success = FALSE;
-	}
 #ifdef __APPLE__
-	if (g_chmod(fname, 0777) != 0)
+	if (success && g_chmod(fname, 0777) != 0)
 	{
-		if (error && ! *error) /* don't set error twice */
-			set_file_error_from_errno(error, errno, "Failed to make file executable");
+		if (error)
+		{
+			gint errsv = errno;
+
+			g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errsv),
+					"Failed to make file executable: %s", g_strerror(errsv));
+		}
 		success = FALSE;
 	}
 #endif
 
-	return success;
+	if (!success)
+	{
+		g_unlink(fname);
+		g_free(fname);
+		fname = NULL;
+	}
+
+	return fname;
 }
 
 



--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).


More information about the Commits mailing list