[geany/geany] 5f8791: Merge pull request #1095 from eht16/issue1076_win32_build_working_dir_locale
Colomban Wendling
git-noreply at geany.org
Sun Nov 13 15:29:43 UTC 2016
Branch: refs/heads/master
Author: Colomban Wendling <ban at herbesfolles.org>
Committer: Colomban Wendling <ban at herbesfolles.org>
Date: Sun, 13 Nov 2016 15:29:43 UTC
Commit: 5f879126cecd443d2e1a4be4eacaf8ac1d43ffa8
https://github.com/geany/geany/commit/5f879126cecd443d2e1a4be4eacaf8ac1d43ffa8
Log Message:
-----------
Merge pull request #1095 from eht16/issue1076_win32_build_working_dir_locale
Windows: Improve executing external commands on non-ASCII paths,
effectively fixing most known issues.
Modified Paths:
--------------
geany.nsi.in
src/Makefile.am
src/build.c
src/geany-run-helper.bat
src/prefix.h
src/spawn.c
src/utils.c
src/utils.h
src/win32.c
Modified: geany.nsi.in
4 lines changed, 4 insertions(+), 0 deletions(-)
===================================================================
@@ -137,6 +137,9 @@ Section "!Program Files" SEC01
File "gtk\bin\libstdc++-*.dll"
File "gtk\bin\libwinpthread*.dll"
+ SetOutPath "$INSTDIR\libexec"
+ File /r "${RESOURCEDIR}\libexec\*"
+
SetOutPath "$INSTDIR\data"
File "${RESOURCEDIR}\data\GPL-2"
File "${RESOURCEDIR}\data\filetype_extensions.conf"
@@ -311,6 +314,7 @@ Section Uninstall
RMDir /r "$INSTDIR\etc"
RMDir /r "$INSTDIR\include"
RMDir /r "$INSTDIR\lib"
+ RMDir /r "$INSTDIR\libexec"
RMDir /r "$INSTDIR\share"
RMDir "$INSTDIR"
Modified: src/Makefile.am
7 lines changed, 7 insertions(+), 0 deletions(-)
===================================================================
@@ -138,6 +138,7 @@ AM_CPPFLAGS += \
-DGEANY_DATADIR=\"data\" \
-DGEANY_DOCDIR=\"\" \
-DGEANY_LIBDIR=\"\" \
+ -DGEANY_LIBEXECDIR=\"\" \
-DGEANY_LOCALEDIR=\"\" \
-DGEANY_PREFIX=\"\"
@@ -162,6 +163,7 @@ AM_CPPFLAGS += \
-DGEANY_DATADIR=\""$(datadir)"\" \
-DGEANY_DOCDIR=\""$(docdir)"\" \
-DGEANY_LIBDIR=\""$(libdir)"\" \
+ -DGEANY_LIBEXECDIR=\""$(libexecdir)"\" \
-DGEANY_LOCALEDIR=\""$(localedir)"\" \
-DGEANY_PREFIX=\""$(prefix)"\"
@@ -182,6 +184,11 @@ signallist.i: $(glade_file) Makefile
CLEANFILES += signallist.i
+# install the run script
+if MINGW
+dist_pkglibexec_SCRIPTS = geany-run-helper.bat
+endif
+
# Ubuntu ld has a bug so that libtool sees /usr/local/lib as a system path so
# doesn't add RPATH, but ld requires explicit ldconfig there, unlike when
# installing in /usr/lib. So, workaround this by calling it explicitly when
Modified: src/build.c
55 lines changed, 38 insertions(+), 17 deletions(-)
===================================================================
@@ -79,9 +79,7 @@ typedef struct RunInfo
static RunInfo *run_info;
-#ifdef G_OS_WIN32
-static const gchar RUN_SCRIPT_CMD[] = "geany_run_script_XXXXXX.bat";
-#else
+#ifndef G_OS_WIN32
static const gchar RUN_SCRIPT_CMD[] = "geany_run_script_XXXXXX.sh";
#endif
@@ -115,7 +113,9 @@ static guint build_items_count = 9;
static void build_exit_cb(GPid pid, gint status, gpointer user_data);
static void build_iofunc(GString *string, GIOCondition condition, gpointer data);
+#ifndef G_OS_WIN32
static gchar *build_create_shellscript(const gchar *working_dir, const gchar *cmd, gboolean autoclose, GError **error);
+#endif
static void 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);
@@ -750,13 +750,15 @@ static void build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *d
msgwin_compiler_add(COLOR_BLUE, _("%s (in directory: %s)"), cmd, utf8_working_dir);
g_free(utf8_working_dir);
+#ifdef G_OS_UNIX
cmd_string = utils_get_locale_from_utf8(cmd);
argv[2] = cmd_string;
-
-#ifdef G_OS_UNIX
cmd = NULL; /* under Unix, use argv to start cmd via sh for compatibility */
#else
+ /* Expand environment variables like %blah%. */
+ cmd_string = win32_expand_environment_variables(cmd);
argv[0] = NULL; /* under Windows, run cmd directly */
+ cmd = cmd_string;
#endif
/* set the build info for the message window */
@@ -826,6 +828,17 @@ static gchar *prepare_run_cmd(GeanyDocument *doc, gchar **working_dir, guint cmd
}
#endif
+#ifdef G_OS_WIN32
+ /* Expand environment variables like %blah%. */
+ SETPTR(cmd_string, win32_expand_environment_variables(cmd_string));
+
+ gchar *helper = g_build_filename(utils_resource_dir(RESOURCE_DIR_LIBEXEC), "geany-run-helper", NULL);
+ /* escape helper appropriately */
+ /* FIXME: check the Windows rules, but it should not matter too much here as \es and "es are not
+ * allowed in paths anyway */
+ run_cmd = g_strdup_printf("\"%s\" \"%s\" %d %s", helper, *working_dir, autoclose ? 1 : 0, cmd_string);
+ g_free(helper);
+#else
run_cmd = build_create_shellscript(*working_dir, cmd_string, autoclose, &error);
if (!run_cmd)
{
@@ -834,7 +847,7 @@ static gchar *prepare_run_cmd(GeanyDocument *doc, gchar **working_dir, guint cmd
g_error_free(error);
g_free(*working_dir);
}
-
+#endif
utils_free_pointers(3, cmd_string_utf8, working_dir_utf8, cmd_string, NULL);
return run_cmd;
}
@@ -892,6 +905,21 @@ static void build_run_cmd(GeanyDocument *doc, guint cmdindex)
gchar *locale_term_cmd = utils_get_locale_from_utf8(tool_prefs.term_cmd);
GError *error = NULL;
+#ifdef G_OS_WIN32
+ if (g_regex_match_simple("^[ \"]*cmd([.]exe)?[\" ]", locale_term_cmd, 0, 0))
+ {
+ /* if passing an argument to cmd.exe, respect its quoting rules */
+ GString *escaped_run_cmd = g_string_new(NULL);
+ for (gchar *p = run_cmd; *p; p++)
+ {
+ if (strchr("()%!^\"<>&| ", *p)) // cmd.exe metacharacters
+ g_string_append_c(escaped_run_cmd, '^');
+ g_string_append_c(escaped_run_cmd, *p);
+ }
+ SETPTR(run_cmd, g_string_free(escaped_run_cmd, FALSE));
+ }
+#endif
+
utils_str_replace_all(&locale_term_cmd, "%c", run_cmd);
if (spawn_async(working_dir, locale_term_cmd, NULL, NULL, &(run_info[cmdindex].pid),
@@ -908,7 +936,9 @@ static void build_run_cmd(GeanyDocument *doc, guint cmdindex)
"Check the Terminal setting in Preferences"), utf8_term_cmd, error->message);
g_free(utf8_term_cmd);
g_error_free(error);
+#ifndef G_OS_WIN32
g_unlink(run_cmd);
+#endif
run_info[cmdindex].pid = (GPid) 0;
}
}
@@ -1063,35 +1093,25 @@ static void run_exit_cb(GPid child_pid, gint status, gpointer user_data)
/* write a little shellscript to call the executable (similar to anjuta_launcher but "internal")
* 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 */
+#ifndef G_OS_WIN32
static gchar *build_create_shellscript(const gchar *working_dir, const gchar *cmd, gboolean autoclose, GError **error)
{
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);
-#ifdef G_OS_WIN32
- /* Expand environment variables like %blah%. */
- expanded_cmd = win32_expand_environment_variables(cmd);
- 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_shell_quote(working_dir);
str = g_strdup_printf(
"#!/bin/sh\n\nrm $0\n\ncd %s\n\n%s\n\necho \"\n\n------------------\n(program exited with code: $?)\" \
\n\n%s\n", escaped_dir, cmd, (autoclose) ? "" :
"\necho \"Press return to continue\"\n#to be more compatible with shells like "
"dash\ndummy_var=\"\"\nread dummy_var");
g_free(escaped_dir);
-#endif
if (!g_file_set_contents(fname, str, -1, error))
success = FALSE;
@@ -1119,6 +1139,7 @@ static gchar *build_create_shellscript(const gchar *working_dir, const gchar *cm
return fname;
}
+#endif
typedef void Callback(GtkWidget *w, gpointer u);
Modified: src/geany-run-helper.bat
30 lines changed, 30 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,30 @@
+REM USAGE: geany-run-helper DIRECTORY AUTOCLOSE COMMAND...
+
+REM unnecessary, but we get the directory
+cd %1
+shift
+REM save autoclose option and remove it
+set autoclose=%1
+shift
+
+REM spawn the child
+REM it's tricky because shift doesn't affect %*, so hack it out
+REM https://en.wikibooks.org/wiki/Windows_Batch_Scripting#Command-line_arguments
+set SPAWN=
+:argloop
+if -%1-==-- goto argloop_end
+ set SPAWN=%SPAWN% %1
+ shift
+goto argloop
+:argloop_end
+%SPAWN%
+
+REM show the result
+echo:
+echo:
+echo:------------------
+echo:(program exited with code: %ERRORLEVEL%)
+echo:
+
+REM and if wanted, wait on the user
+if not %autoclose%==1 pause
Modified: src/prefix.h
1 lines changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -81,6 +81,7 @@ G_BEGIN_DECLS
#define GEANY_PREFIX (br_thread_local_store (br_locate_prefix ((void *) "")))
#define GEANY_DATADIR (br_thread_local_store (br_prepend_prefix ((void *) "", "/share")))
#define GEANY_LIBDIR (br_thread_local_store (br_prepend_prefix ((void *) "", "/lib")))
+ #define GEANY_LIBEXECDIR (br_thread_local_store (br_prepend_prefix ((void *) "", "/libexec/geany")))
#define GEANY_DOCDIR (br_thread_local_store (br_prepend_prefix ((void *) "", "/share/doc/geany")))
#define GEANY_LOCALEDIR (br_thread_local_store (br_prepend_prefix ((void *) "", "/share/locale")))
#endif /* BR_NO_MACROS */
Modified: src/spawn.c
132 lines changed, 112 insertions(+), 20 deletions(-)
===================================================================
@@ -307,11 +307,11 @@ gboolean spawn_kill_process(GPid pid, GError **error)
#ifdef G_OS_WIN32
-static gchar *spawn_create_process_with_pipes(char *command_line, const char *working_directory,
- void *environment, HANDLE *hprocess, int *stdin_fd, int *stdout_fd, int *stderr_fd)
+static gchar *spawn_create_process_with_pipes(wchar_t *w_command_line, const wchar_t *w_working_directory,
+ void *w_environment, HANDLE *hprocess, int *stdin_fd, int *stdout_fd, int *stderr_fd)
{
enum { WRITE_STDIN, READ_STDOUT, READ_STDERR, READ_STDIN, WRITE_STDOUT, WRITE_STDERR };
- STARTUPINFO startup;
+ STARTUPINFOW startup;
PROCESS_INFORMATION process;
HANDLE hpipe[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
int *fd[3] = { stdin_fd, stdout_fd, stderr_fd };
@@ -372,8 +372,9 @@ static gchar *spawn_create_process_with_pipes(char *command_line, const char *wo
startup.hStdOutput = hpipe[WRITE_STDOUT];
startup.hStdError = hpipe[WRITE_STDERR];
- if (!CreateProcess(NULL, command_line, NULL, NULL, TRUE, pipe_io ? CREATE_NO_WINDOW : 0,
- environment, working_directory, &startup, &process))
+ if (!CreateProcessW(NULL, w_command_line, NULL, NULL, TRUE,
+ CREATE_UNICODE_ENVIRONMENT | (pipe_io ? CREATE_NO_WINDOW : 0),
+ w_environment, w_working_directory, &startup, &process))
{
failed = ""; /* report the message only */
/* further errors will not be reported */
@@ -521,8 +522,10 @@ static gboolean spawn_async_with_pipes(const gchar *working_directory, const gch
#ifdef G_OS_WIN32
GString *command;
- GArray *environment;
- gchar *failure;
+ GArray *w_environment;
+ wchar_t *w_working_directory = NULL;
+ wchar_t *w_command = NULL;
+ gboolean success = TRUE;
if (command_line)
{
@@ -553,7 +556,7 @@ static gboolean spawn_async_with_pipes(const gchar *working_directory, const gch
else
command = g_string_new(NULL);
- environment = g_array_new(TRUE, FALSE, sizeof(char));
+ w_environment = g_array_new(TRUE, FALSE, sizeof(wchar_t));
while (argv && *argv)
spawn_append_argument(command, *argv++);
@@ -562,26 +565,115 @@ static gboolean spawn_async_with_pipes(const gchar *working_directory, const gch
g_message("full spawn command line: %s", command->str);
#endif
- while (envp && *envp)
+ while (envp && *envp && success)
{
- g_array_append_vals(environment, *envp, strlen(*envp) + 1);
+ glong w_entry_len;
+ wchar_t *w_entry;
+ gchar *tmp = NULL;
+
+ // FIXME: remove this and rely on UTF-8 input
+ if (! g_utf8_validate(*envp, -1, NULL))
+ {
+ tmp = g_locale_to_utf8(*envp, -1, NULL, NULL, NULL);
+ if (tmp)
+ *envp = tmp;
+ }
+ /* TODO: better error message */
+ w_entry = g_utf8_to_utf16(*envp, -1, NULL, &w_entry_len, error);
+
+ if (! w_entry)
+ success = FALSE;
+ else
+ {
+ /* copy the entry, including NUL terminator */
+ g_array_append_vals(w_environment, w_entry, w_entry_len + 1);
+ g_free(w_entry);
+ }
+
+ g_free(tmp);
envp++;
}
- failure = spawn_create_process_with_pipes(command->str, working_directory,
- envp ? environment->data : NULL, child_pid, stdin_fd, stdout_fd, stderr_fd);
+ /* convert working directory into locale encoding */
+ if (success && working_directory)
+ {
+ GError *gerror = NULL;
+ const gchar *utf8_working_directory;
+ gchar *tmp = NULL;
- g_string_free(command, TRUE);
- g_array_free(environment, TRUE);
+ // FIXME: remove this and rely on UTF-8 input
+ if (! g_utf8_validate(working_directory, -1, NULL))
+ {
+ tmp = g_locale_to_utf8(working_directory, -1, NULL, NULL, NULL);
+ if (tmp)
+ utf8_working_directory = tmp;
+ }
+ else
+ utf8_working_directory = working_directory;
- if (failure)
+ w_working_directory = g_utf8_to_utf16(utf8_working_directory, -1, NULL, NULL, &gerror);
+ if (! w_working_directory)
+ {
+ /* TODO use the code below post-1.28 as it introduces a new string
+ g_set_error(error, gerror->domain, gerror->code,
+ _("Failed to convert working directory into locale encoding: %s"), gerror->message);
+ */
+ g_propagate_error(error, gerror);
+ success = FALSE;
+ }
+ g_free(tmp);
+ }
+ /* convert command into locale encoding */
+ if (success)
{
- g_set_error_literal(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, failure);
- g_free(failure);
- return FALSE;
+ GError *gerror = NULL;
+ const gchar *utf8_cmd;
+ gchar *tmp = NULL;
+
+ // FIXME: remove this and rely on UTF-8 input
+ if (! g_utf8_validate(command->str, -1, NULL))
+ {
+ tmp = g_locale_to_utf8(command->str, -1, NULL, NULL, NULL);
+ if (tmp)
+ utf8_cmd = tmp;
+ }
+ else
+ utf8_cmd = command->str;
+
+ w_command = g_utf8_to_utf16(utf8_cmd, -1, NULL, NULL, &gerror);
+ if (! w_command)
+ {
+ /* TODO use the code below post-1.28 as it introduces a new string
+ g_set_error(error, gerror->domain, gerror->code,
+ _("Failed to convert command into locale encoding: %s"), gerror->message);
+ */
+ g_propagate_error(error, gerror);
+ success = FALSE;
+ }
}
- return TRUE;
+ if (success)
+ {
+ gchar *failure;
+
+ failure = spawn_create_process_with_pipes(w_command, w_working_directory,
+ envp ? w_environment->data : NULL, child_pid, stdin_fd, stdout_fd, stderr_fd);
+
+ if (failure)
+ {
+ g_set_error_literal(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, failure);
+ g_free(failure);
+ }
+
+ success = failure == NULL;
+ }
+
+ g_string_free(command, TRUE);
+ g_array_free(w_environment, TRUE);
+ g_free(w_working_directory);
+ g_free(w_command);
+
+ return success;
#else /* G_OS_WIN32 */
int cl_argc;
char **full_argv;
@@ -655,7 +747,7 @@ static gboolean spawn_async_with_pipes(const gchar *working_directory, const gch
#ifdef ENFILE
case G_SPAWN_ERROR_NFILE : en = ENFILE; break;
#endif
- #ifdef EMFILE
+ #ifdef EMFILE
case G_SPAWN_ERROR_MFILE : en = EMFILE; break;
#endif
#ifdef EINVAL
Modified: src/utils.c
3 lines changed, 3 insertions(+), 0 deletions(-)
===================================================================
@@ -2103,6 +2103,7 @@ const gchar *utils_resource_dir(GeanyResourceDirType type)
resdirs[RESOURCE_DIR_DOC] = g_build_filename(prefix, "share", "doc", "geany", "html", NULL);
resdirs[RESOURCE_DIR_LOCALE] = g_build_filename(prefix, "share", "locale", NULL);
resdirs[RESOURCE_DIR_PLUGIN] = g_build_filename(prefix, "lib", "geany", NULL);
+ resdirs[RESOURCE_DIR_LIBEXEC] = g_build_filename(prefix, "libexec", "geany", NULL);
g_free(prefix);
#else
if (is_osx_bundle())
@@ -2115,6 +2116,7 @@ const gchar *utils_resource_dir(GeanyResourceDirType type)
resdirs[RESOURCE_DIR_DOC] = g_build_filename(prefix, "share", "doc", "geany", "html", NULL);
resdirs[RESOURCE_DIR_LOCALE] = g_build_filename(prefix, "share", "locale", NULL);
resdirs[RESOURCE_DIR_PLUGIN] = g_build_filename(prefix, "lib", "geany", NULL);
+ resdirs[RESOURCE_DIR_LIBEXEC] = g_build_filename(prefix, "libexec", "geany", NULL);
g_free(prefix);
# endif
}
@@ -2125,6 +2127,7 @@ const gchar *utils_resource_dir(GeanyResourceDirType type)
resdirs[RESOURCE_DIR_DOC] = g_build_filename(GEANY_DOCDIR, "html", NULL);
resdirs[RESOURCE_DIR_LOCALE] = g_build_filename(GEANY_LOCALEDIR, NULL);
resdirs[RESOURCE_DIR_PLUGIN] = g_build_filename(GEANY_LIBDIR, "geany", NULL);
+ resdirs[RESOURCE_DIR_LIBEXEC] = g_build_filename(GEANY_LIBEXECDIR, "geany", NULL);
}
#endif
}
Modified: src/utils.h
1 lines changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -221,6 +221,7 @@ typedef enum
RESOURCE_DIR_DOC,
RESOURCE_DIR_LOCALE,
RESOURCE_DIR_PLUGIN,
+ RESOURCE_DIR_LIBEXEC,
RESOURCE_DIR_COUNT
} GeanyResourceDirType;
Modified: src/win32.c
15 lines changed, 10 insertions(+), 5 deletions(-)
===================================================================
@@ -891,14 +891,19 @@ void win32_init_debug_code(void)
}
+/* expands environment placeholders in @str. input and output is in UTF-8 */
gchar *win32_expand_environment_variables(const gchar *str)
{
- gchar expCmdline[32768]; /* 32768 is the limit for ExpandEnvironmentStrings() */
+ wchar_t *cmdline = g_utf8_to_utf16(str, -1, NULL, NULL, NULL);
+ wchar_t expCmdline[32768]; /* 32768 is the limit for ExpandEnvironmentStrings() */
+ gchar *expanded = NULL;
- if (ExpandEnvironmentStrings((LPCTSTR) str, (LPTSTR) expCmdline, sizeof(expCmdline)) != 0)
- return g_strdup(expCmdline);
- else
- return g_strdup(str);
+ if (cmdline && ExpandEnvironmentStringsW(cmdline, expCmdline, sizeof(expCmdline)) != 0)
+ expanded = g_utf16_to_utf8(expCmdline, -1, NULL, NULL, NULL);
+
+ g_free(cmdline);
+
+ return expanded ? expanded : g_strdup(str);
}
--------------
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