From e93211c50d2c42100d76f63a658e39568e3959be Mon Sep 17 00:00:00 2001 From: Kirill Date: Tue, 2 Sep 2008 22:01:39 -0400 Subject: [PATCH] Implement and use varargs interface to mingw_spawnvpe The current version: - supports WAITMODE and QUIETMODE; - allows to collect stdout and stderr into strbufs; and generally resembles start_command from /git/run-command.c. --- debug.h | 2 + exec.c | 294 +++++++++++++++++++++++++++++++++++++++++----------------------- exec.h | 39 +++++---- menu.c | 15 ++-- 4 files changed, 225 insertions(+), 125 deletions(-) rewrite exec.c (65%) rewrite exec.h (95%) diff --git a/debug.h b/debug.h index e237793..14fa27d 100644 --- a/debug.h +++ b/debug.h @@ -4,6 +4,8 @@ #define DEFAULT_DEBUG_GIT_FILE "C:/git_shell_ext_debug.txt" void debug_git_set_file(const char * filename); + +typedef void reporter(char *format, ...); void debug_git(char * format, ...); void debug_git_mbox(char *format, ...); diff --git a/exec.c b/exec.c dissimilarity index 65% index c0fa819..45a9757 100644 --- a/exec.c +++ b/exec.c @@ -1,105 +1,189 @@ -#include -#include -#include -#include "debug.h" -#include "systeminfo.h" -#include "exec.h" - -#define MAX_PROCESSING_TIME (60 * 1000) - -char *env_for_git() -{ - static char *environment; - - /* - * if we can't find path to msys in the registry, return NULL and - * the CreateProcess will copy the environment for us - */ - if (!environment && msys_path()) { - char *old = GetEnvironmentStrings(); - size_t space = 0, path_index = -1, name_len = 0, len2; - - while (old[space]) { - /* if it's PATH variable (could be Path= too!) */ - if (!strnicmp(old + space, "PATH=", 5)) { - path_index = space; - name_len = 5; - } - - while (old[space]) - space++; - space++; /* skip var-terminating NULL */ - } - - if (path_index == -1) - path_index = space; - - environment = malloc(space + - 2 * strlen(msys_path()) + 32); - - /* copy the block up to the equal sign of PATH var */ - memcpy(environment, old, path_index); - /* insert new segments of the PATH */ - len2 = sprintf(environment + path_index, - "PATH=%s\\bin;%s\\mingw\\bin%s", - msys_path(), msys_path(), name_len ? ";" : ""); - /* append original value of PATH and variables after it */ - memcpy(environment + path_index + len2, - old + path_index + name_len, - space + 1 - path_index - name_len); - - FreeEnvironmentStrings(old); - } - - return environment; -} - -int exec_git(char *command, const char *wd, int mode) -{ - STARTUPINFO si = { sizeof(si) }; - PROCESS_INFORMATION pi; - DWORD status = -1; - char cmd[1024]; - - if (!msys_path()) { - debug_git("[ERROR] Could not find msysPath"); - return -1; - } - - sprintf(cmd, "\"%s\\bin\\git.exe\" %s", msys_path(), command); - - si.dwFlags = STARTF_USESHOWWINDOW; - si.wShowWindow = SW_HIDE; - - debug_git("Trying to spawn 'git %s' in working directory '%s'\n", - command, wd); - if (!CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, - env_for_git(), wd, &si, &pi)) { - debug_git("[ERROR] Could not create process (%d); " - "wd: %s; cmd: %s", - GetLastError(), wd, command); - - return -1; - } - - if (P_WAIT == mode) { - if (WAIT_OBJECT_0 == WaitForSingleObject(pi.hProcess, - MAX_PROCESSING_TIME)) { - - if (!GetExitCodeProcess(pi.hProcess, &status)) - debug_git("[ERROR] GetExitCode failed (%d); " - "wd: %s; cmd: %s", - GetLastError(), wd, command); - - } else - debug_git("[ERROR] process timed out; " - "wd: %s; cmd: %s", - wd, command); - } - - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - - return status; -} - +#include "cache.h" + +#include "debug.h" +#include "systeminfo.h" +#include "exec.h" + +#define MAX_PROCESSING_TIME (60 * 1000) +#define MAX_ARGS 32 + +/* + * builds an array of environment variables, + * as expected by mingw_spawnvpe + */ +static char **env_for_git() +{ + static char **environment; + + /* + * if we can't find path to msys in the registry, return NULL and + * the CreateProcess will copy the environment for us + */ + if (!environment && msys_path()) { + char *old = GetEnvironmentStrings(); + size_t space = 0, path_index = -1, name_len = 0; + int total = 0, i; + + while (old[space]) { + /* if it's PATH variable (could be Path= too!) */ + if (!strnicmp(old + space, "PATH=", 5)) { + path_index = space; + name_len = 5; + } + + while (old[space]) + space++; + space++; /* skip var-terminating NULL */ + + total++; + } + + if (path_index == -1) + path_index = space; + + environment = malloc(sizeof(*environment) * (total + 1)); + space = 0; + for (i = 0; i < total; i++) { + if (path_index == space) { + char *path = old + space + name_len; + size_t len; + environment[i] = malloc(strlen(path) + 1 + + 2 * strlen(msys_path()) + 32); + len = sprintf(environment[i], + "PATH=%s\\bin;%s\\mingw\\bin%s%s", + msys_path(), msys_path(), + *path ? ";" : "", path); + } else + environment[i] = strdup(old + space); + + while (old[space]) + space++; + space++; /* skip var-terminating NULL */ + } + + /* mark the end of the array */ + environment[i] = 0; + + FreeEnvironmentStrings(old); + } + + return environment; +} + +/* copy from run-command.c */ +static inline void close_pair(int fd[2]) +{ + close(fd[0]); + close(fd[1]); +} + +int exec_program(const char *working_directory, + struct strbuf *output, struct strbuf *error_output, + int flags, ...) +{ + int fdout[2], fderr[2]; + int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */ + + va_list params; + const char *argv[MAX_ARGS]; + char *arg; + int argc = 0; + + pid_t pid; + DWORD status = 0; + + reporter *debug = QUIETMODE & flags ? debug_git : debug_git_mbox; + + if (!msys_path()) { + debug("[ERROR] Could not find msysPath"); + return -1; + } + + if (output) { + if (pipe(fdout) < 0) { + return -ERR_RUN_COMMAND_PIPE; + } + s1 = dup(1); + dup2(fdout[1], 1); + + flags |= WAITMODE; + } + + if (error_output) { + if (pipe(fderr) < 0) { + if (output) + close_pair(fdout); + return -ERR_RUN_COMMAND_PIPE; + } + s2 = dup(2); + dup2(fderr[1], 2); + + flags |= WAITMODE; + } + + va_start(params, flags); + do { + arg = va_arg(params, char*); + argv[argc++] = arg; + } while (argc < MAX_ARGS && arg); + va_end(params); + + pid = mingw_spawnvpe(argv[0], argv, env_for_git()); + + if (s1 >= 0) + dup2(s1, 1), close(s1); + if (s2 >= 0) + dup2(s2, 2), close(s2); + + if (pid < 0) { + if (output) + close_pair(fdout); + if (error_output) + close_pair(fderr); + return -ERR_RUN_COMMAND_FORK; + } + + if (output) + close(fdout[1]); + if (error_output) + close(fderr[1]); + + if (WAITMODE & flags) { + if (WAIT_OBJECT_0 == WaitForSingleObject((HANDLE)pid, + MAX_PROCESSING_TIME)) { + if (GetExitCodeProcess((HANDLE)pid, &status)) + debug_git("Exit code: %d", status); + else { + /* play safe, and return total failure */ + status = -1; + debug_git("[ERROR] GetExitCode failed (%d); " + "wd: %s; cmd: %s", + GetLastError(), + working_directory, + argv[0]); + } + + if (output) { + strbuf_read(output, fdout[0], 0); + debug_git("STDOUT:\r\n%s\r\n*** end of STDOUT ***\r\n", output->buf); + } + + if (error_output) { + strbuf_read(error_output, fderr[0], 0); + debug_git("STDERR:\r\n%s\r\n*** end of STDERR ***\r\n", error_output->buf); + } + } else { + status = -ERR_RUN_COMMAND_WAITPID_NOEXIT; + debug_git("[ERROR] process timed out; " + "wd: %s; cmd: %s", + working_directory, argv[0]); + } + } + + if (output) + close(fdout[0]); + if (error_output) + close(fderr[0]); + + return status; +} diff --git a/exec.h b/exec.h dissimilarity index 95% index 31f23a5..90af80e 100644 --- a/exec.h +++ b/exec.h @@ -1,14 +1,25 @@ - -/* - * Modifies a copy of the environment to include Git in PATH - */ -char *env_for_git(); - -/* - * Native, simplified implementation of spawn with a working directory - * - * Executes git.exe - * Supports only P_WAIT and P_NOWAIT modes. - */ -int exec_git(char *cmd, const char *wd, int mode); - + +#define NORMALMODE (0) +#define HIDDENMODE (1 << 0) /* hide any windows, opened by execution */ +#define WAITMODE (1 << 1) /* wait for execution to complete */ +#define QUIETMODE (1 << 7) /* don't report any errors to user */ + +enum { + ERR_RUN_COMMAND_FORK = 10000, + ERR_RUN_COMMAND_EXEC, + ERR_RUN_COMMAND_PIPE, + ERR_RUN_COMMAND_WAITPID, + ERR_RUN_COMMAND_WAITPID_WRONG_PID, + ERR_RUN_COMMAND_WAITPID_SIGNAL, + ERR_RUN_COMMAND_WAITPID_NOEXIT, +}; + +/* + * Varargs interface to mingw_spawnvpe with NULL as end-of-format indicator. + * + * - supports specifying working directory; + * - supports different modes of operation (see NORMALMODE & co. above). + */ +int exec_program(const char *working_directory, + struct strbuf *output, struct strbuf *error_output, + int flags, ...); diff --git a/menu.c b/menu.c index b8098d2..566b7e8 100644 --- a/menu.c +++ b/menu.c @@ -1,7 +1,7 @@ +#include "cache.h" + #include #include -#include -#include #include "menu.h" #include "ext.h" #include "debug.h" @@ -41,10 +41,12 @@ static STDMETHODIMP query_context_menu(void *p, HMENU menu, } } - status = exec_git("rev-parse --show-cdup", wd, P_WAIT); + status = exec_program(wd, NULL, NULL, WAITMODE, + "git", "rev-parse", "--show-cdup", NULL); free (wd); - if (-1 == status) + /* something really bad happened, could run git */ + if (status < 0) return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0); /* @@ -52,7 +54,7 @@ static STDMETHODIMP query_context_menu(void *p, HMENU menu, * build_menu_items() * It's left as is to signify the preview nature of the patch */ - if (0 != status) { /* this is not a repository */ + if (status) { /* this is not a repository */ if (bDirSelected) strcpy(menu_item, "&Git Clone Here"); else @@ -150,7 +152,8 @@ static STDMETHODIMP invoke_command(void *p, if (!(fa & dwAttr)) wd = info->lpDirectory; - exec_git("gui", wd, P_NOWAIT); + exec_program(wd, NULL, NULL, NORMALMODE, + "git", "gui", NULL); return S_OK; } -- 2.11.4.GIT