aboutsummaryrefslogtreecommitdiff
path: root/run-command.c
diff options
context:
space:
mode:
Diffstat (limited to 'run-command.c')
-rw-r--r--run-command.c66
1 files changed, 56 insertions, 10 deletions
diff --git a/run-command.c b/run-command.c
index 1c7a3b611..15e2e74a7 100644
--- a/run-command.c
+++ b/run-command.c
@@ -267,6 +267,55 @@ static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
}
}
}
+
+static char **prep_childenv(const char *const *deltaenv)
+{
+ extern char **environ;
+ char **childenv;
+ struct string_list env = STRING_LIST_INIT_DUP;
+ struct strbuf key = STRBUF_INIT;
+ const char *const *p;
+ int i;
+
+ /* Construct a sorted string list consisting of the current environ */
+ for (p = (const char *const *) environ; p && *p; p++) {
+ const char *equals = strchr(*p, '=');
+
+ if (equals) {
+ strbuf_reset(&key);
+ strbuf_add(&key, *p, equals - *p);
+ string_list_append(&env, key.buf)->util = (void *) *p;
+ } else {
+ string_list_append(&env, *p)->util = (void *) *p;
+ }
+ }
+ string_list_sort(&env);
+
+ /* Merge in 'deltaenv' with the current environ */
+ for (p = deltaenv; p && *p; p++) {
+ const char *equals = strchr(*p, '=');
+
+ if (equals) {
+ /* ('key=value'), insert or replace entry */
+ strbuf_reset(&key);
+ strbuf_add(&key, *p, equals - *p);
+ string_list_insert(&env, key.buf)->util = (void *) *p;
+ } else {
+ /* otherwise ('key') remove existing entry */
+ string_list_remove(&env, *p, 0);
+ }
+ }
+
+ /* Create an array of 'char *' to be used as the childenv */
+ childenv = xmalloc((env.nr + 1) * sizeof(char *));
+ for (i = 0; i < env.nr; i++)
+ childenv[i] = env.items[i].util;
+ childenv[env.nr] = NULL;
+
+ string_list_clear(&env, 0);
+ strbuf_release(&key);
+ return childenv;
+}
#endif
static inline void set_cloexec(int fd)
@@ -395,12 +444,14 @@ fail_pipe:
#ifndef GIT_WINDOWS_NATIVE
{
int notify_pipe[2];
+ char **childenv;
struct argv_array argv = ARGV_ARRAY_INIT;
if (pipe(notify_pipe))
notify_pipe[0] = notify_pipe[1] = -1;
prepare_cmd(&argv, cmd);
+ childenv = prep_childenv(cmd->env);
cmd->pid = fork();
failed_errno = errno;
@@ -456,14 +507,6 @@ fail_pipe:
if (cmd->dir && chdir(cmd->dir))
die_errno("exec '%s': cd to '%s' failed", cmd->argv[0],
cmd->dir);
- if (cmd->env) {
- for (; *cmd->env; cmd->env++) {
- if (strchr(*cmd->env, '='))
- putenv((char *)*cmd->env);
- else
- unsetenv(*cmd->env);
- }
- }
/*
* Attempt to exec using the command and arguments starting at
@@ -471,9 +514,11 @@ fail_pipe:
* be used in the event exec failed with ENOEXEC at which point
* we will try to interpret the command using 'sh'.
*/
- execv(argv.argv[1], (char *const *) argv.argv + 1);
+ execve(argv.argv[1], (char *const *) argv.argv + 1,
+ (char *const *) childenv);
if (errno == ENOEXEC)
- execv(argv.argv[0], (char *const *) argv.argv);
+ execve(argv.argv[0], (char *const *) argv.argv,
+ (char *const *) childenv);
if (errno == ENOENT) {
if (!cmd->silent_exec_failure)
@@ -509,6 +554,7 @@ fail_pipe:
close(notify_pipe[0]);
argv_array_clear(&argv);
+ free(childenv);
}
#else
{