Revision: 2279 http://geany.svn.sourceforge.net/geany/?rev=2279&view=rev Author: eht16 Date: 2008-02-24 02:27:32 -0800 (Sun, 24 Feb 2008)
Log Message: ----------- Apply patch by Pierre Joye to add new process spawning implementation for Windows. Make utils_spawn_* available to plugin API. This makes the VCdiff plugin to work on Windows (thanks).
Modified Paths: -------------- trunk/ChangeLog trunk/THANKS trunk/plugins/vcdiff.c trunk/src/plugindata.h trunk/src/plugins.c trunk/src/utils.c trunk/src/utils.h trunk/src/win32.c trunk/src/win32.h
Modified: trunk/ChangeLog =================================================================== --- trunk/ChangeLog 2008-02-24 10:22:35 UTC (rev 2278) +++ trunk/ChangeLog 2008-02-24 10:27:32 UTC (rev 2279) @@ -5,6 +5,11 @@ * configure.in: Apply patch Yura Siamashka to add a check for the presence of fnmatch(), thanks. + * THANKS, plugins/vcdiff.c, src/plugindata.h, src/plugins.c, + src/utils.c, src/utils.h, src/win32.c, src/win32.h: + Apply patch by Pierre Joye to add new process spawning implementation + for Windows. Make utils_spawn_* available to plugin API. + This makes the VCdiff plugin to work on Windows (thanks).
2008-02-22 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
Modified: trunk/THANKS =================================================================== --- trunk/THANKS 2008-02-24 10:22:35 UTC (rev 2278) +++ trunk/THANKS 2008-02-24 10:27:32 UTC (rev 2279) @@ -41,6 +41,7 @@ Bo Lorentsen <bl(at)lue(dot)dk> - project session patch Yura Siamashka <yurand2(at)gmail(dot)com> - many patches Daniel Richard G. <skunk(at)iskunk(dot)org> - some patches +Pierre Joye <pierre(dot)php(at)gmail(dot)com> - Win32 process spawning patch
Translators: ------------
Modified: trunk/plugins/vcdiff.c =================================================================== --- trunk/plugins/vcdiff.c 2008-02-24 10:22:35 UTC (rev 2278) +++ trunk/plugins/vcdiff.c 2008-02-24 10:27:32 UTC (rev 2279) @@ -36,6 +36,7 @@ #include "project.h" #include "pluginmacros.h"
+ PluginFields *plugin_fields; GeanyData *geany_data;
@@ -186,7 +187,7 @@ }
-static void* get_cmd_env(gint cmd_type, gboolean cmd, const gchar* filename) +static void* get_cmd_env(gint cmd_type, gboolean cmd, const gchar* filename, int *size) { int i; gint len = 0; @@ -234,6 +235,9 @@ else ret[i] = g_strdup(argv[i]); } + + *size = len; + g_free(dir); g_free(base_filename); return ret; @@ -312,13 +316,15 @@ { gchar *std_output = NULL; gchar *std_error = NULL; - gint exit_code; gchar *text = NULL; gchar *dir; - gchar **env = get_cmd_env(cmd, FALSE, filename); - gchar **argv = get_cmd_env(cmd, TRUE, filename); + gint argc = 0; + gchar **env = get_cmd_env(cmd, FALSE, filename, &argc); + gchar **argv = get_cmd_env(cmd, TRUE, filename, &argc); + gint exit_code = 0; + GError *error = NULL;
- if (!argv) + if (! argv) { if (env) g_strfreev(env); @@ -334,9 +340,10 @@ dir = g_path_get_dirname(filename); }
- if (g_spawn_sync(dir, argv, env, G_SPAWN_SEARCH_PATH, NULL, NULL, &std_output, &std_error, &exit_code, NULL)) + if (p_utils->spawn_sync(dir, argv, env, G_SPAWN_SEARCH_PATH, NULL, NULL, + &std_output, &std_error, &exit_code, &error)) { - // CVS dump stuff to stderr when diff nested dirs + /* CVS dump stuff to stderr when diff nested dirs */ if (strcmp(argv[0], "cvs") != 0 && NZV(std_error)) { p_dialogs->show_msgbox(1, @@ -350,12 +357,29 @@ { p_ui->set_statusbar(FALSE, _("No changes were made.")); } + + if (NZV(std_error)) + { + p_dialogs->show_msgbox(1, _("VCdiff command sent errors:\n%s\n."), std_error); + } } else { - p_ui->set_statusbar(FALSE, - _("Something went really wrong.")); + gchar *msg; + + if (error != NULL) + { + msg = g_strdup(error->message); + g_error_free(error); + } + else + { /* if we don't have an exact error message, print at least the failing command */ + msg = g_strdup_printf(_("unknown error while trying to spawn a process for %s"), + argv[0]); + } + p_ui->set_statusbar(FALSE, _("An error occurred (%s)."), msg); } + g_free(dir); g_free(std_error); g_strfreev(env);
Modified: trunk/src/plugindata.h =================================================================== --- trunk/src/plugindata.h 2008-02-24 10:22:35 UTC (rev 2278) +++ trunk/src/plugindata.h 2008-02-24 10:27:32 UTC (rev 2279) @@ -36,7 +36,7 @@
/* The API version should be incremented whenever any plugin data types below are * modified or appended to. */ -static const gint api_version = 44; +static const gint api_version = 45;
/* The ABI version should be incremented whenever existing fields in the plugin * data types below have to be changed or reordered. It should stay the same if fields @@ -241,6 +241,12 @@ const gint default_value); gchar* (*get_setting_string) (GKeyFile *config, const gchar *section, const gchar *key, const gchar *default_value); + gboolean (*spawn_sync) (const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags, + GSpawnChildSetupFunc child_setup, gpointer user_data, gchar **std_out, + gchar **std_err, gint *exit_status, GError **error); + gboolean (*spawn_async) (const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags, + GSpawnChildSetupFunc child_setup, gpointer user_data, GPid *child_pid, + GError **error); } UtilsFuncs;
Modified: trunk/src/plugins.c =================================================================== --- trunk/src/plugins.c 2008-02-24 10:22:35 UTC (rev 2278) +++ trunk/src/plugins.c 2008-02-24 10:27:32 UTC (rev 2279) @@ -153,7 +153,9 @@ &utils_mkdir, &utils_get_setting_boolean, &utils_get_setting_integer, - &utils_get_setting_string + &utils_get_setting_string, + &utils_spawn_sync, + &utils_spawn_async };
static UIUtilsFuncs uiutils_funcs = {
Modified: trunk/src/utils.c =================================================================== --- trunk/src/utils.c 2008-02-24 10:22:35 UTC (rev 2278) +++ trunk/src/utils.c 2008-02-24 10:27:32 UTC (rev 2279) @@ -1793,3 +1793,111 @@ }
+static gboolean check_error(GError **error) +{ + if (error != NULL && *error != NULL) + { + /* imitate the GLib warning */ + g_warning( + "GError set over the top of a previous GError or uninitialized memory.\n" + "This indicates a bug in someone's code. You must ensure an error is NULL " + "before it's set."); + /* after returning the code may segfault, but we don't care because we should + * make sure *error is NULL */ + return FALSE; + } + return TRUE; +} + + +/** + * This is a wrapper function for g_spawn_sync() and internally calls this function on Unix-like + * systems. On Win32 platforms, it uses the Windows API. + * + * @param dir The child's current working directory, or @a NULL to inherit parent's. + * @param argv The child's argument vector. + * @param env The child's environment, or @a NULL to inherit parent's. + * @param flags Flags from GSpawnFlags. + * @param child_setup A function to run in the child just before exec(). + * @param user_data The user data for child_setup. + * @param std_out The return location for child output. + * @param std_err The return location for child error messages. + * @param exit_status The child exit status, as returned by waitpid(). + * @param error The return location for error or @a NULL. + * + * @return @a TRUE on success, @a FALSE if an error was set. + **/ +gboolean utils_spawn_sync(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags, + GSpawnChildSetupFunc child_setup, gpointer user_data, gchar **std_out, + gchar **std_err, gint *exit_status, GError **error) +{ + gboolean result; + + if (! check_error(error)) + return FALSE; + + if (argv == NULL) + { + *error = g_error_new(G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "argv must not be NULL"); + return FALSE; + } + + if (std_out) + *std_out = NULL; + + if (std_err) + *std_err = NULL; + +#ifdef G_OS_WIN32 + result = win32_spawn(dir, argv, env, flags, std_out, std_err, exit_status); + /** TODO create error messages in win32_spawn with appropriate error message text **/ + if (! result) + *error = g_error_new(G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, _("Process could not be created.")); +#else + result = g_spawn_sync(dir, argv, env, flags, NULL, NULL, std_out, std_err, exit_status, error); +#endif + + return result; +} + + +/** + * This is a wrapper function for g_spawn_async() and internally calls this function on Unix-like + * systems. On Win32 platforms, it uses the Windows API. + * + * @param dir The child's current working directory, or @a NULL to inherit parent's. + * @param argv The child's argument vector. + * @param env The child's environment, or @a NULL to inherit parent's. + * @param flags Flags from GSpawnFlags. + * @param child_setup A function to run in the child just before exec(). + * @param user_data The user data for child_setup. + * @param child_pid The return location for child process ID, or NULL. + * @param error The return location for error or @a NULL. + * + * @return @a TRUE on success, @a FALSE if an error was set. + **/ +gboolean utils_spawn_async(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags, + GSpawnChildSetupFunc child_setup, gpointer user_data, GPid *child_pid, + GError **error) +{ + gboolean result; + + if (! check_error(error)) + return FALSE; + + if (argv == NULL) + { + *error = g_error_new(G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "argv must not be NULL"); + return FALSE; + } + +#ifdef G_OS_WIN32 + result = win32_spawn(dir, argv, env, flags, NULL, NULL, NULL); + /** TODO create error messages in win32_spawn with appropriate error message text **/ + if (! result) + *error = g_error_new(G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, _("Process could not be created.")); +#else + result = g_spawn_async(dir, argv, env, flags, NULL, NULL, child_pid, error); +#endif + return result; +}
Modified: trunk/src/utils.h =================================================================== --- trunk/src/utils.h 2008-02-24 10:22:35 UTC (rev 2278) +++ trunk/src/utils.h 2008-02-24 10:27:32 UTC (rev 2279) @@ -146,4 +146,13 @@
gint utils_is_file_writeable(const gchar *locale_filename);
+ +gboolean utils_spawn_sync(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags, + GSpawnChildSetupFunc child_setup, gpointer user_data, gchar **std_out, + gchar **std_err, gint *exit_status, GError **error); + +gboolean utils_spawn_async(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags, + GSpawnChildSetupFunc child_setup, gpointer user_data, GPid *child_pid, + GError **error); + #endif
Modified: trunk/src/win32.c =================================================================== --- trunk/src/win32.c 2008-02-24 10:22:35 UTC (rev 2278) +++ trunk/src/win32.c 2008-02-24 10:27:32 UTC (rev 2279) @@ -54,8 +54,29 @@ #include "dialogs.h" #include "filetypes.h"
+#define BUFSIZE 4096
+struct _geany_win32_spawn +{ + HANDLE hChildStdinRd; + HANDLE hChildStdinWr; + HANDLE hChildStdoutRd; + HANDLE hChildStdoutWr; + HANDLE hChildStderrRd; + HANDLE hChildStderrWr; + HANDLE hInputFile; + HANDLE hStdout; + HANDLE hStderr; +}; +typedef struct _geany_win32_spawn geany_win32_spawn;
+ +static gboolean GetContentFromHandle(HANDLE hFile, gchar **content); +static HANDLE GetTempFileHandle(void); +static gboolean CreateChildProcess(geany_win32_spawn *gw_spawn, TCHAR *szCmdline, const TCHAR *dir); +static VOID ReadFromPipe(HANDLE hRead, HANDLE hWrite, HANDLE hFile); + + static gchar *get_file_filters() { gchar *string; @@ -313,7 +334,7 @@ if (CommDlgExtendedError()) { gchar error[100]; - snprintf(error, sizeof error, "File dialog box error (%x)", (int)CommDlgExtendedError()); + g_snprintf(error, sizeof error, "File dialog box error (%x)", (int)CommDlgExtendedError()); win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error); } g_free(fname); @@ -482,7 +503,7 @@ if (CommDlgExtendedError()) { gchar error[100]; - snprintf(error, sizeof error, "File dialog box error (%x)", (int)CommDlgExtendedError()); + g_snprintf(error, sizeof error, "File dialog box error (%x)", (int)CommDlgExtendedError()); win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error); } g_strfreev(field); @@ -712,4 +733,295 @@ return g_strdup("localhost"); }
+ +/* Process spawning implementation for Windows, by Pierre Joye. + * Don't call this function directly, use utils_spawn_[a]sync() instead. */ +gboolean win32_spawn(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags, + gchar **std_out, gchar **std_err, gint *exit_status) +{ + TCHAR buffer[MAX_PATH]=TEXT(""); + TCHAR cmdline[MAX_PATH] = TEXT(""); + TCHAR* lpPart[MAX_PATH]={NULL}; + DWORD retval=0; + gint argc = 0, i; + gint cmdpos=0; + + SECURITY_ATTRIBUTES saAttr; + BOOL fSuccess; + geany_win32_spawn gw_spawn; + + /* Temp file */ + HANDLE hStdoutTempFile = NULL; + HANDLE hStderrTempFile = NULL; + + gchar *stdout_content = NULL; + gchar *stderr_content = NULL; + + while (argv[argc]) + { + ++argc; + } + g_return_val_if_fail (std_out == NULL || + !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE); + g_return_val_if_fail (std_err == NULL || + !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE); + + if (flags & G_SPAWN_SEARCH_PATH) + { + retval = SearchPath(NULL, + argv[0], ".exe", MAX_PATH, buffer, lpPart); + g_snprintf(cmdline, MAX_PATH, ""%s"", buffer); + cmdpos = 1; + } + + for (i = cmdpos; i < argc; i++) + { + g_snprintf(cmdline, MAX_PATH, "%s %s", cmdline, argv[i]); + /*MessageBox(NULL, cmdline, cmdline, MB_OK);*/ + } + + if (std_err != NULL) + { + hStderrTempFile = GetTempFileHandle(); + if (hStderrTempFile == INVALID_HANDLE_VALUE) + { + geany_debug("win32_spawn: Second CreateFile failed (%d)\n", (gint) GetLastError()); + return FALSE; + } + } + + if (std_out != NULL) + { + hStdoutTempFile = GetTempFileHandle(); + if (hStdoutTempFile == INVALID_HANDLE_VALUE) + { + geany_debug("win32_spawn: Second CreateFile failed (%d)\n", (gint) GetLastError()); + return FALSE; + } + } + + /* Set the bInheritHandle flag so pipe handles are inherited. */ + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + /* Get the handle to the current STDOUT and STDERR. */ + gw_spawn.hStdout = GetStdHandle(STD_OUTPUT_HANDLE); + gw_spawn.hStderr = GetStdHandle(STD_ERROR_HANDLE); + + /* Create a pipe for the child process's STDOUT. */ + if (! CreatePipe(&(gw_spawn.hChildStdoutRd), &(gw_spawn.hChildStdoutWr), &saAttr, 0)) + { + geany_debug("win32_spawn: Stdout pipe creation failed\n"); + return FALSE; + } + + /* Ensure that the read handle to the child process's pipe for STDOUT is not inherited.*/ + SetHandleInformation(gw_spawn.hChildStdoutRd, HANDLE_FLAG_INHERIT, 0); + + /* Create a pipe for the child process's STDERR. */ + if (!CreatePipe(&(gw_spawn.hChildStderrRd), &(gw_spawn.hChildStderrWr), &saAttr, 0)) + { + geany_debug("win32_spawn: Stdout pipe creation failed\n"); + return FALSE; + } + + /* Ensure that the read handle to the child process's pipe for STDOUT is not inherited.*/ + SetHandleInformation(gw_spawn.hChildStderrRd, HANDLE_FLAG_INHERIT, 0); + + /* Create a pipe for the child process's STDIN. */ + if (!CreatePipe(&(gw_spawn.hChildStdinRd), &(gw_spawn.hChildStdinWr), &saAttr, 0)) + { + geany_debug("win32_spawn: Stdin pipe creation failed\n"); + return FALSE; + } + + /* Ensure that the write handle to the child process's pipe for STDIN is not inherited. */ + SetHandleInformation(gw_spawn.hChildStdinWr, HANDLE_FLAG_INHERIT, 0); + + + /* Now create the child process. */ + fSuccess = CreateChildProcess(&gw_spawn, cmdline, dir); + if (!fSuccess) + { + geany_debug("win32_spawn: Create process failed with"); + return FALSE; + } + + /* Read from pipe that is the standard output for child process. */ + if (std_out != NULL) + { + ReadFromPipe(gw_spawn.hChildStdoutRd, gw_spawn.hChildStdoutWr, hStdoutTempFile); + GetContentFromHandle(hStdoutTempFile, &stdout_content); + *std_out = stdout_content; + } + + if (std_err != NULL) + { + ReadFromPipe(gw_spawn.hChildStderrRd, gw_spawn.hChildStderrWr, hStderrTempFile); + GetContentFromHandle(hStderrTempFile, &stderr_content); + *std_err = stderr_content; + } + + return TRUE; +} + + +static gboolean GetContentFromHandle(HANDLE hFile, gchar **content) +{ + DWORD filesize; + gchar * buffer; + DWORD dwRead; + + filesize = GetFileSize(hFile, NULL); + if (filesize < 1) + { + *content = NULL; + return TRUE; + } + + buffer = g_malloc(sizeof(gchar*) * (filesize+1)); + if (!buffer) + { + geany_debug("GetContentFromHandle: Alloc failed"); + return FALSE; + } + + SetFilePointer(hFile,0, NULL, FILE_BEGIN); + if (!ReadFile(hFile, buffer, filesize, &dwRead, + NULL) || dwRead == 0) + { + geany_debug("GetContentFromHandle: Cannot read tempfile"); + return FALSE; + } + + if (!CloseHandle (hFile)) + { + geany_debug("GetContentFromHandle: CloseHandle failed (%d)\n", (gint) GetLastError()); + g_free(buffer); + *content = NULL; + return FALSE; + } + *content = buffer; + return TRUE; +} + + +static gboolean CreateChildProcess(geany_win32_spawn *gw_spawn, TCHAR *szCmdline, const TCHAR *dir) +{ + PROCESS_INFORMATION piProcInfo; + STARTUPINFO siStartInfo; + BOOL bFuncRetn = FALSE; + + /* Set up members of the PROCESS_INFORMATION structure. */ + ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION) ); + + /* Set up members of the STARTUPINFO structure.*/ + ZeroMemory(&siStartInfo, sizeof(STARTUPINFO) ); + + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.hStdError = gw_spawn->hChildStderrWr; + siStartInfo.hStdOutput = gw_spawn->hChildStdoutWr; + siStartInfo.hStdInput = gw_spawn->hChildStdinRd; + siStartInfo.dwFlags |= STARTF_USESTDHANDLES; + + /* Create the child process. */ + bFuncRetn = CreateProcess(NULL, + szCmdline, /* command line */ + NULL, /* process security attributes */ + NULL, /* primary thread security attributes */ + TRUE, /* handles are inherited */ + CREATE_NO_WINDOW, /* creation flags */ + NULL, /* use parent's environment */ + dir, /* use parent's current directory */ + &siStartInfo, /* STARTUPINFO pointer */ + &piProcInfo); /* receives PROCESS_INFORMATION */ + + if (bFuncRetn == 0) + { + geany_debug("CreateChildProcess: CreateProcess failed\n"); + return FALSE; + } + else + { + CloseHandle(piProcInfo.hProcess); + CloseHandle(piProcInfo.hThread); + return bFuncRetn; + } + return FALSE; +} + + +static VOID ReadFromPipe(HANDLE hRead, HANDLE hWrite, HANDLE hFile) +{ + DWORD dwRead, dwWritten; + CHAR chBuf[BUFSIZE]; + + /* Close the write end of the pipe before reading from the + read end of the pipe. */ + if (!CloseHandle(hWrite)) + { + geany_debug("ReadFromPipe: Closing handle failed"); + return; + } + + /* Read output from the child process, and write to parent's STDOUT. */ + for (;;) + { + if( !ReadFile(hRead, chBuf, BUFSIZE, &dwRead, + NULL) || dwRead == 0) break; + + if (!WriteFile(hFile, chBuf, dwRead, &dwWritten, NULL)) + break; + } +} + + +static HANDLE GetTempFileHandle(void) +{ + /* Temp file */ + DWORD dwBufSize=BUFSIZE; + UINT uRetVal; + TCHAR szTempName[BUFSIZE]; + TCHAR lpPathBuffer[BUFSIZE]; + DWORD dwRetVal; + HANDLE hTempFile; + + /* Get the temp path. */ + dwRetVal = GetTempPath(dwBufSize, /* length of the buffer*/ + lpPathBuffer); /* buffer for path */ + + if (dwRetVal > dwBufSize || (dwRetVal == 0)) + { + geany_debug("GetTempFileHandle: GetTempPath failed (%d)\n", (gint) GetLastError()); + return NULL; + } + + /* Create a temporary file for STDOUT. */ + uRetVal = GetTempFileName(lpPathBuffer, /* directory for tmp files */ + TEXT("GEANY_VCDIFF_"), /* temp file name prefix */ + 0, /* create unique name */ + szTempName); /* buffer for name */ + if (uRetVal == 0) + { + geany_debug("GetTempFileName failed (%d)\n", (gint) GetLastError()); + return NULL; + } + + hTempFile = CreateFile((LPTSTR) szTempName, /* file name */ + GENERIC_READ | GENERIC_WRITE, /* open r-w */ + 0, /* do not share */ + NULL, /* default security */ + CREATE_ALWAYS, /* overwrite existing */ + FILE_ATTRIBUTE_NORMAL,/* normal file */ + NULL); /* no template */ + + if (hTempFile == INVALID_HANDLE_VALUE) + { + geany_debug("GetTempFileHandle: Second CreateFile failed (%d)\n", (gint) GetLastError()); + return NULL; + } + return hTempFile; +} + #endif
Modified: trunk/src/win32.h =================================================================== --- trunk/src/win32.h 2008-02-24 10:22:35 UTC (rev 2278) +++ trunk/src/win32.h 2008-02-24 10:27:32 UTC (rev 2279) @@ -61,4 +61,7 @@
gchar *win32_get_hostname();
+gboolean win32_spawn(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags, + gchar **std_out, gchar **std_err, gint *exit_status); + #endif
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.