From 2de27f2cbb43b30d5465be33541c98b453a076e6 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 29 Dec 2006 09:01:17 +0100 Subject: [PATCH] Implement a wrapper of execve that can invoke shell scripts. When an external git command is invoked, it can be a Bourne shell script. This patch looks into the command file to see whether it is one. In this case, the command line is rearranged to invoke the shell with the proper arguments. Moreover, the arguments are quoted if necessary because Windows' spawn functions paste the arguments again into a command line that is disassembled by the invoked process. --- compat/mingw.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++ git-compat-util.h | 2 ++ 2 files changed, 90 insertions(+) diff --git a/compat/mingw.c b/compat/mingw.c index f22ab57461..18aecd9bf5 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -139,3 +139,91 @@ void sync(void) void openlog(const char *ident, int option, int facility) { } + +static const char *quote_arg(const char *arg) +{ + /* count chars to quote */ + int len = 0, n = 0; + int force_quotes = 0; + char *q, *d; + const char *p = arg; + while (*p) { + if (isspace(*p)) + force_quotes = 1; + else if (*p == '"' || *p == '\\') + n++; + len++; + p++; + } + if (!force_quotes && n == 0) + return arg; + + /* insert \ where necessary */ + d = q = xmalloc(len+n+3); + *d++ = '"'; + while (*arg) { + if (*arg == '"' || *arg == '\\') + *d++ = '\\'; + *d++ = *arg++; + } + *d++ = '"'; + *d++ = 0; + return q; +} + +static void quote_argv(const char **dst, const char **src) +{ + while (*src) + *dst++ = quote_arg(*src++); + *dst = NULL; +} + +static int try_shell_exec(const char *cmd, const char **argv, const char **env) +{ + char buf[100], *p; + const char **sh_argv; + int n; + int fd = open(cmd, O_RDONLY); + if (fd < 0) + return 0; + n = read(fd, buf, sizeof(buf)-1); + close(fd); + if (n < 5) /* at least '#!/sh' and not error */ + return 0; + + /* check whether the interpreter is sh */ + if (buf[0] != '#' || buf[1] != '!') + return 0; + buf[n] = '\0'; + p = strchr(buf, '\n'); + if (!p || + (p[-3] != '/' && p[-3] != '\\') || + p[-2] != 's' || p[-1] != 'h') + return 0; + + /* + * expand + * git-foo args... + * into + * sh git-foo args... + */ + for (n = 0; argv[n];) n++; + sh_argv = xmalloc((n+2)*sizeof(char*)); + sh_argv[0] = "sh"; + sh_argv[1] = cmd; + quote_argv(&sh_argv[2], &argv[1]); + n = spawnvpe(_P_WAIT, "sh", sh_argv, env); + if (n == -1) + return 1; /* indicate that we tried but failed */ + exit(n); +} + +void mingw_execve(const char *cmd, const char **argv, const char **env) +{ + /* check if git_command is a shell script */ + if (!try_shell_exec(cmd, argv, env)) { + int ret = spawnve(_P_WAIT, cmd, argv, env); + if (ret != -1) + exit(ret); + } +} diff --git a/git-compat-util.h b/git-compat-util.h index d361fcaee8..4b213815b7 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -306,6 +306,8 @@ int syslog(int type, char *bufp, ...); #define LOG_DAEMON 4 unsigned int alarm(unsigned int seconds); #include +void mingw_execve(const char *cmd, const char **argv, const char **env); +#define execve mingw_execve int fork(); typedef int pid_t; pid_t waitpid(pid_t pid, int *status, int options);