diff options
author | Johannes Sixt <johannes.sixt@telecom.at> | 2007-12-07 22:08:59 +0100 |
---|---|---|
committer | Johannes Sixt <johannes.sixt@telecom.at> | 2008-06-23 13:40:31 +0200 |
commit | ba26f296f9ddc694fc42683132bc328dffd777ec (patch) | |
tree | 203758fc03abfaaa7f873ed12055d7d4187021df | |
parent | 897bb8cb2c2ce6b73038bd8d4106fde079a09cf6 (diff) | |
download | git-ba26f296f9ddc694fc42683132bc328dffd777ec.tar.gz |
Windows: Implement start_command().
On Windows, we have spawnv() variants to run a child process instead of
fork()/exec(). In order to attach pipe ends to stdin, stdout, and stderr,
we have to use this idiom:
save1 = dup(1);
dup2(pipe[1], 1);
spawnv();
dup2(save1, 1);
close(pipe[1]);
assuming that the descriptors created by pipe() are not inheritable.
Signed-off-by: Johannes Sixt <johannes.sixt@telecom.at>
-rw-r--r-- | compat/mingw.c | 62 | ||||
-rw-r--r-- | compat/mingw.h | 8 | ||||
-rw-r--r-- | run-command.c | 97 |
3 files changed, 153 insertions, 14 deletions
diff --git a/compat/mingw.c b/compat/mingw.c index 31a9b9e251..7f89a6cb87 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -315,6 +315,68 @@ void mingw_execvp(const char *cmd, char *const *argv) free_path_split(path); } +char **copy_environ() +{ + char **env; + int i = 0; + while (environ[i]) + i++; + env = xmalloc((i+1)*sizeof(*env)); + for (i = 0; environ[i]; i++) + env[i] = xstrdup(environ[i]); + env[i] = NULL; + return env; +} + +void free_environ(char **env) +{ + int i; + for (i = 0; env[i]; i++) + free(env[i]); + free(env); +} + +static int lookup_env(char **env, const char *name, size_t nmln) +{ + int i; + + for (i = 0; env[i]; i++) { + if (0 == strncmp(env[i], name, nmln) + && '=' == env[i][nmln]) + /* matches */ + return i; + } + return -1; +} + +/* + * If name contains '=', then sets the variable, otherwise it unsets it + */ +char **env_setenv(char **env, const char *name) +{ + char *eq = strchrnul(name, '='); + int i = lookup_env(env, name, eq-name); + + if (i < 0) { + if (*eq) { + for (i = 0; env[i]; i++) + ; + env = xrealloc(env, (i+2)*sizeof(*env)); + env[i] = xstrdup(name); + env[i+1] = NULL; + } + } + else { + free(env[i]); + if (*eq) + env[i] = xstrdup(name); + else + for (; env[i]; i++) + env[i] = env[i+1]; + } + return env; +} + #undef rename int mingw_rename(const char *pold, const char *pnew) { diff --git a/compat/mingw.h b/compat/mingw.h index 0ce9c96f93..0879894c68 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -165,3 +165,11 @@ sig_handler_t mingw_signal(int sig, sig_handler_t handler); #define is_dir_sep(c) ((c) == '/' || (c) == '\\') #define PATH_SEP ';' #define PRIuMAX "I64u" + +/* + * helpers + */ + +char **copy_environ(void); +void free_environ(char **env); +char **env_setenv(char **env, const char *name); diff --git a/run-command.c b/run-command.c index 44100a749b..dd8b7751ce 100644 --- a/run-command.c +++ b/run-command.c @@ -65,21 +65,8 @@ int start_command(struct child_process *cmd) cmd->err = fderr[0]; } +#ifndef __MINGW32__ cmd->pid = fork(); - if (cmd->pid < 0) { - if (need_in) - close_pair(fdin); - else if (cmd->in) - close(cmd->in); - if (need_out) - close_pair(fdout); - else if (cmd->out) - close(cmd->out); - if (need_err) - close_pair(fderr); - return -ERR_RUN_COMMAND_FORK; - } - if (!cmd->pid) { if (cmd->no_stdin) dup_devnull(0); @@ -128,6 +115,88 @@ int start_command(struct child_process *cmd) } die("exec %s failed.", cmd->argv[0]); } +#else + int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */ + const char *sargv0 = cmd->argv[0]; + char **env = environ; + struct strbuf git_cmd; + + if (cmd->no_stdin) { + s0 = dup(0); + dup_devnull(0); + } else if (need_in) { + s0 = dup(0); + dup2(fdin[0], 0); + } else if (cmd->in) { + s0 = dup(0); + dup2(cmd->in, 0); + } + + if (cmd->no_stderr) { + s2 = dup(2); + dup_devnull(2); + } else if (need_err) { + s2 = dup(2); + dup2(fderr[1], 2); + } + + if (cmd->no_stdout) { + s1 = dup(1); + dup_devnull(1); + } else if (cmd->stdout_to_stderr) { + s1 = dup(1); + dup2(2, 1); + } else if (need_out) { + s1 = dup(1); + dup2(fdout[1], 1); + } else if (cmd->out > 1) { + s1 = dup(1); + dup2(cmd->out, 1); + } + + if (cmd->dir) + die("chdir in start_command() not implemented"); + if (cmd->env) { + env = copy_environ(); + for (; *cmd->env; cmd->env++) + env = env_setenv(env, *cmd->env); + } + + if (cmd->git_cmd) { + strbuf_init(&git_cmd, 0); + strbuf_addf(&git_cmd, "git-%s", cmd->argv[0]); + cmd->argv[0] = git_cmd.buf; + } + + cmd->pid = spawnvpe(_P_NOWAIT, cmd->argv[0], cmd->argv, (const char **)env); + + if (cmd->env) + free_environ(env); + if (cmd->git_cmd) + strbuf_release(&git_cmd); + + cmd->argv[0] = sargv0; + if (s0 >= 0) + dup2(s0, 0), close(s0); + if (s1 >= 0) + dup2(s1, 1), close(s1); + if (s2 >= 0) + dup2(s2, 2), close(s2); +#endif + + if (cmd->pid < 0) { + if (need_in) + close_pair(fdin); + else if (cmd->in) + close(cmd->in); + if (need_out) + close_pair(fdout); + else if (cmd->out) + close(cmd->out); + if (need_err) + close_pair(fderr); + return -ERR_RUN_COMMAND_FORK; + } if (need_in) close(fdin[0]); |