Branch: refs/heads/master Author: Dimitar Zhekov dimitar.zhekov@gmail.com Committer: Dimitar Zhekov dimitar.zhekov@gmail.com Date: Wed, 01 Apr 2015 18:16:23 UTC Commit: 8745ba58ae8e5582de7b3c828ac1eefa4e62e134 https://github.com/geany/geany/commit/8745ba58ae8e5582de7b3c828ac1eefa4e62e1...
Log Message: ----------- Fix Windows attempts to run 2+ unquoted tokens as a program
When passed Foo Bar Qux as a command line, Windows will try to run Foo.exe Bar Qux, then "Foo Bar.exe" Qux, and last "Foo Bar Qux.exe", for maximum flexibility. Quoting Foo supresses this behaviour.
Modified Paths: -------------- src/spawn.c src/spawn.h
Modified: src/spawn.c 79 lines changed, 62 insertions(+), 17 deletions(-) =================================================================== @@ -75,7 +75,7 @@
/** - * Checks whether a command line is valid. + * Checks whether a command line is syntactically valid and extracts the program name from it. * * All OS: * - any leading spaces and tabs are ignored @@ -84,20 +84,20 @@ * - the standard shell quoting and escaping rules are used, see @c g_shell_parse_argv() * - as a consequence, an unqouted # at the start of an argument comments to the end of line * Windows: - * - a quoted program name must be entirely inside the quotes. It's hard to believe, but - * "C:\Foo\Bar".pdf is actually executed as "C:\Foo\Bar.exe", and .pdf is completely - * ignored, not even passed as an argument - * - the standard Windows quoting rules are used: double quote is escaped with slash, and - * any literal slashes before a double quote must be duplicated. + * - a quoted program name must be entirely inside the quotes. No "C:\Foo\Bar".pdf or + * "C:\Foo\Bar".bat, which would be executed by Windows as C:\Foo\Bar.exe + * - an unquoted program name may not contain spaces. Foo Bar Qux will not be considered + * "Foo Bar.exe" Qux or "Foo Bar Qux.exe", depending on what executables exist, as + * Windows normally does. + * - the standard Windows quoting and escaping rules are used: double quote is escaped with + * backslash, and any literal backslashes before a double quote must be duplicated. * - * @param command_line the command line to check. - * @param execute whether to check, using @c g_find_program_in_path(), if the program specified - * in the command line exists and is executable. + * @param command_line the command line to check and get the program name from. * @param error return location for error. * - * @return @c TRUE on success, @c FALSE if an error was set. + * @return allocated string with the program name on success, @c NULL on error. **/ -gboolean spawn_check_command(const gchar *command_line, gboolean execute, GError **error) +gchar *spawn_get_program_name(const gchar *command_line, GError **error) { gchar *program;
@@ -189,6 +189,31 @@ gboolean spawn_check_command(const gchar *command_line, gboolean execute, GError g_strfreev(argv); #endif /* G_OS_WIN32 */
+ return program; +} + + +/** + * Checks whether a command line is valid. + * + * Checks if @a command_line is syntactically valid using @c spawn_get_program_name(). + * + * If @a execute is TRUE, also checks, using @c g_find_program_in_path(), if the program + * specified in @a command_line exists and is executable. + * + * @param command_line the command line to check. + * @param execute whether to check if the command line is really executable. + * @param error return location for error. + * + * @return @c TRUE on success, @c FALSE on error. + */ +gboolean spawn_check_command(const gchar *command_line, gboolean execute, GError **error) +{ + gchar *program = spawn_get_program_name(command_line, error); + + if (!program) + return FALSE; + if (execute) { gchar *executable = g_find_program_in_path(program); @@ -445,20 +470,40 @@ gboolean spawn_async_with_pipes(const gchar *working_directory, const gchar *com
#ifdef G_OS_WIN32 GString *command; - GArray *environment = g_array_new(TRUE, FALSE, sizeof(char)); + GArray *environment; GPid pid; gchar *failure;
if (command_line) { - if (!spawn_check_command(command_line, FALSE, error)) + gchar *program = spawn_get_program_name(command_line, error); + const gchar *arguments; + + if (!program) return FALSE; - /* checked command line contains at least 1 non-blank */ - while (strchr(CL_BLANKS, *command_line)) - command_line++; + + command = g_string_new(NULL); + arguments = strstr(command_line, program) + strlen(program); + + if (*arguments == '"') + { + g_string_append(command, program); + arguments++; + } + else + { + /* quote the first token, to avoid Windows attemps to run two or more + unquoted tokens as a program until an existing file name is found */ + g_string_printf(command, ""%s"", program); + } + + g_string_append(command, arguments); + g_free(program); } + else + command = g_string_new(NULL);
- command = g_string_new(command_line); + environment = g_array_new(TRUE, FALSE, sizeof(char));
while (argv && *argv) spawn_append_argument(command, *argv++);
Modified: src/spawn.h 2 lines changed, 2 insertions(+), 0 deletions(-) =================================================================== @@ -24,6 +24,8 @@
#include <glib.h>
+gchar *spawn_get_program_name(const gchar *command_line, GError **error); + gboolean spawn_check_command(const gchar *command_line, gboolean execute, GError **error);
gboolean spawn_kill_process(GPid pid, GError **error);
-------------- This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).