Skip to content

Commit 20574f5

Browse files
peffgitster
authored andcommitted
prepare_{git,shell}_cmd: use argv_array
These functions transform an existing argv into one suitable for exec-ing or spawning via git or a shell. We can use an argv_array in each to avoid dealing with manual counting and allocation. This also makes the memory allocation more clear and fixes some leaks. In prepare_shell_cmd, we would sometimes allocate a new string with "$@" in it and sometimes not, meaning the caller could not correctly free it. On the non-Windows side, we are in a child process which will exec() or exit() immediately, so the leak isn't a big deal. On Windows, though, we use spawn() from the parent process, and leak a string for each shell command we run. On top of that, the Windows code did not free the allocated argv array at all (but does for the prepare_git_cmd case!). By switching both of these functions to write into an argv_array, we can consistently free the result as appropriate. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 50a6c8e commit 20574f5

3 files changed

Lines changed: 39 additions & 53 deletions

File tree

exec_cmd.c

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "cache.h"
22
#include "exec_cmd.h"
33
#include "quote.h"
4+
#include "argv-array.h"
45
#define MAX_ARGS 32
56

67
static const char *argv_exec_path;
@@ -107,32 +108,25 @@ void setup_path(void)
107108
strbuf_release(&new_path);
108109
}
109110

110-
const char **prepare_git_cmd(const char **argv)
111+
const char **prepare_git_cmd(struct argv_array *out, const char **argv)
111112
{
112-
int argc;
113-
const char **nargv;
114-
115-
for (argc = 0; argv[argc]; argc++)
116-
; /* just counting */
117-
nargv = xmalloc(sizeof(*nargv) * (argc + 2));
118-
119-
nargv[0] = "git";
120-
for (argc = 0; argv[argc]; argc++)
121-
nargv[argc + 1] = argv[argc];
122-
nargv[argc + 1] = NULL;
123-
return nargv;
113+
argv_array_push(out, "git");
114+
argv_array_pushv(out, argv);
115+
return out->argv;
124116
}
125117

126118
int execv_git_cmd(const char **argv) {
127-
const char **nargv = prepare_git_cmd(argv);
128-
trace_argv_printf(nargv, "trace: exec:");
119+
struct argv_array nargv = ARGV_ARRAY_INIT;
120+
121+
prepare_git_cmd(&nargv, argv);
122+
trace_argv_printf(nargv.argv, "trace: exec:");
129123

130124
/* execvp() can only ever return if it fails */
131-
sane_execvp("git", (char **)nargv);
125+
sane_execvp("git", (char **)nargv.argv);
132126

133127
trace_printf("trace: exec failed: %s\n", strerror(errno));
134128

135-
free(nargv);
129+
argv_array_clear(&nargv);
136130
return -1;
137131
}
138132

exec_cmd.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#ifndef GIT_EXEC_CMD_H
22
#define GIT_EXEC_CMD_H
33

4+
struct argv_array;
5+
46
extern void git_set_argv_exec_path(const char *exec_path);
57
extern const char *git_extract_argv0_path(const char *path);
68
extern const char *git_exec_path(void);
79
extern void setup_path(void);
8-
extern const char **prepare_git_cmd(const char **argv);
10+
extern const char **prepare_git_cmd(struct argv_array *out, const char **argv);
911
extern int execv_git_cmd(const char **argv); /* NULL terminated */
1012
LAST_ARG_MUST_BE_NULL
1113
extern int execl_git_cmd(const char *cmd, ...);

run-command.c

Lines changed: 25 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -158,50 +158,41 @@ int sane_execvp(const char *file, char * const argv[])
158158
return -1;
159159
}
160160

161-
static const char **prepare_shell_cmd(const char **argv)
161+
static const char **prepare_shell_cmd(struct argv_array *out, const char **argv)
162162
{
163-
int argc, nargc = 0;
164-
const char **nargv;
165-
166-
for (argc = 0; argv[argc]; argc++)
167-
; /* just counting */
168-
/* +1 for NULL, +3 for "sh -c" plus extra $0 */
169-
nargv = xmalloc(sizeof(*nargv) * (argc + 1 + 3));
170-
171-
if (argc < 1)
163+
if (!argv[0])
172164
die("BUG: shell command is empty");
173165

174166
if (strcspn(argv[0], "|&;<>()$`\\\"' \t\n*?[#~=%") != strlen(argv[0])) {
175167
#ifndef GIT_WINDOWS_NATIVE
176-
nargv[nargc++] = SHELL_PATH;
168+
argv_array_push(out, SHELL_PATH);
177169
#else
178-
nargv[nargc++] = "sh";
170+
argv_array_push(out, "sh");
179171
#endif
180-
nargv[nargc++] = "-c";
181-
182-
if (argc < 2)
183-
nargv[nargc++] = argv[0];
184-
else {
185-
struct strbuf arg0 = STRBUF_INIT;
186-
strbuf_addf(&arg0, "%s \"$@\"", argv[0]);
187-
nargv[nargc++] = strbuf_detach(&arg0, NULL);
188-
}
189-
}
172+
argv_array_push(out, "-c");
190173

191-
for (argc = 0; argv[argc]; argc++)
192-
nargv[nargc++] = argv[argc];
193-
nargv[nargc] = NULL;
174+
/*
175+
* If we have no extra arguments, we do not even need to
176+
* bother with the "$@" magic.
177+
*/
178+
if (!argv[1])
179+
argv_array_push(out, argv[0]);
180+
else
181+
argv_array_pushf(out, "%s \"$@\"", argv[0]);
182+
}
194183

195-
return nargv;
184+
argv_array_pushv(out, argv);
185+
return out->argv;
196186
}
197187

198188
#ifndef GIT_WINDOWS_NATIVE
199189
static int execv_shell_cmd(const char **argv)
200190
{
201-
const char **nargv = prepare_shell_cmd(argv);
202-
trace_argv_printf(nargv, "trace: exec:");
203-
sane_execvp(nargv[0], (char **)nargv);
204-
free(nargv);
191+
struct argv_array nargv = ARGV_ARRAY_INIT;
192+
prepare_shell_cmd(&nargv, argv);
193+
trace_argv_printf(nargv.argv, "trace: exec:");
194+
sane_execvp(nargv.argv[0], (char **)nargv.argv);
195+
argv_array_clear(&nargv);
205196
return -1;
206197
}
207198
#endif
@@ -455,6 +446,7 @@ int start_command(struct child_process *cmd)
455446
{
456447
int fhin = 0, fhout = 1, fherr = 2;
457448
const char **sargv = cmd->argv;
449+
struct argv_array nargv = ARGV_ARRAY_INIT;
458450

459451
if (cmd->no_stdin)
460452
fhin = open("/dev/null", O_RDWR);
@@ -480,9 +472,9 @@ int start_command(struct child_process *cmd)
480472
fhout = dup(cmd->out);
481473

482474
if (cmd->git_cmd)
483-
cmd->argv = prepare_git_cmd(cmd->argv);
475+
cmd->argv = prepare_git_cmd(&nargv, cmd->argv);
484476
else if (cmd->use_shell)
485-
cmd->argv = prepare_shell_cmd(cmd->argv);
477+
cmd->argv = prepare_shell_cmd(&nargv, cmd->argv);
486478

487479
cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, (char**) cmd->env,
488480
cmd->dir, fhin, fhout, fherr);
@@ -492,9 +484,7 @@ int start_command(struct child_process *cmd)
492484
if (cmd->clean_on_exit && cmd->pid >= 0)
493485
mark_child_for_cleanup(cmd->pid);
494486

495-
if (cmd->git_cmd)
496-
free(cmd->argv);
497-
487+
argv_array_clear(&nargv);
498488
cmd->argv = sargv;
499489
if (fhin != 0)
500490
close(fhin);

0 commit comments

Comments
 (0)