#define MAX_ARGS 32
extern char **environ;
-static const char *builtin_exec_path = GIT_EXEC_PATH;
-static const char *current_exec_path;
+static const char *argv_exec_path;
+static const char *argv0_path;
-void git_set_exec_path(const char *exec_path)
+const char *system_path(const char *path)
{
- current_exec_path = exec_path;
+ if (!is_absolute_path(path) && argv0_path) {
+ struct strbuf d = STRBUF_INIT;
+ strbuf_addf(&d, "%s/%s", argv0_path, path);
+ path = strbuf_detach(&d, NULL);
+ }
+ return path;
+}
+
+void git_set_argv0_path(const char *path)
+{
+ argv0_path = path;
+}
+
+void git_set_argv_exec_path(const char *exec_path)
+{
+ argv_exec_path = exec_path;
}
{
const char *env;
- if (current_exec_path)
- return current_exec_path;
+ if (argv_exec_path)
+ return argv_exec_path;
env = getenv(EXEC_PATH_ENVIRONMENT);
if (env && *env) {
return env;
}
- return builtin_exec_path;
+ return system_path(GIT_EXEC_PATH);
}
+static void add_path(struct strbuf *out, const char *path)
+{
+ if (path && *path) {
+ if (is_absolute_path(path))
+ strbuf_addstr(out, path);
+ else
+ strbuf_addstr(out, make_nonrelative_path(path));
+
+ strbuf_addch(out, PATH_SEP);
+ }
+}
-int execv_git_cmd(const char **argv)
+void setup_path(void)
{
- char git_command[PATH_MAX + 1];
- int i;
- const char *paths[] = { current_exec_path,
- getenv(EXEC_PATH_ENVIRONMENT),
- builtin_exec_path };
-
- for (i = 0; i < ARRAY_SIZE(paths); ++i) {
- size_t len;
- int rc;
- const char *exec_dir = paths[i];
- const char *tmp;
-
- if (!exec_dir || !*exec_dir) continue;
-
- if (*exec_dir != '/') {
- if (!getcwd(git_command, sizeof(git_command))) {
- fprintf(stderr, "git: cannot determine "
- "current directory: %s\n",
- strerror(errno));
- break;
- }
- len = strlen(git_command);
-
- /* Trivial cleanup */
- while (!prefixcmp(exec_dir, "./")) {
- exec_dir += 2;
- while (*exec_dir == '/')
- exec_dir++;
- }
-
- rc = snprintf(git_command + len,
- sizeof(git_command) - len, "/%s",
- exec_dir);
- if (rc < 0 || rc >= sizeof(git_command) - len) {
- fprintf(stderr, "git: command name given "
- "is too long.\n");
- break;
- }
- } else {
- if (strlen(exec_dir) + 1 > sizeof(git_command)) {
- fprintf(stderr, "git: command name given "
- "is too long.\n");
- break;
- }
- strcpy(git_command, exec_dir);
- }
-
- len = strlen(git_command);
- rc = snprintf(git_command + len, sizeof(git_command) - len,
- "/git-%s", argv[0]);
- if (rc < 0 || rc >= sizeof(git_command) - len) {
- fprintf(stderr,
- "git: command name given is too long.\n");
- break;
- }
+ const char *old_path = getenv("PATH");
+ struct strbuf new_path;
- /* argv[0] must be the git command, but the argv array
- * belongs to the caller, and my be reused in
- * subsequent loop iterations. Save argv[0] and
- * restore it on error.
- */
+ strbuf_init(&new_path, 0);
- tmp = argv[0];
- argv[0] = git_command;
+ add_path(&new_path, argv_exec_path);
+ add_path(&new_path, getenv(EXEC_PATH_ENVIRONMENT));
+ add_path(&new_path, system_path(GIT_EXEC_PATH));
+ add_path(&new_path, argv0_path);
- trace_argv_printf(argv, -1, "trace: exec:");
+ if (old_path)
+ strbuf_addstr(&new_path, old_path);
+ else
+ strbuf_addstr(&new_path, "/usr/local/bin:/usr/bin:/bin");
- /* execve() can only ever return if it fails */
- execve(git_command, (char **)argv, environ);
+ setenv("PATH", new_path.buf, 1);
- trace_printf("trace: exec failed: %s\n", strerror(errno));
+ strbuf_release(&new_path);
+}
- argv[0] = tmp;
- }
- return -1;
+const char **prepare_git_cmd(const char **argv)
+{
+ int argc;
+ const char **nargv;
+
+ for (argc = 0; argv[argc]; argc++)
+ ; /* just counting */
+ nargv = xmalloc(sizeof(*nargv) * (argc + 2));
+ nargv[0] = "git";
+ for (argc = 0; argv[argc]; argc++)
+ nargv[argc + 1] = argv[argc];
+ nargv[argc + 1] = NULL;
+ return nargv;
+}
+
+int execv_git_cmd(const char **argv) {
+ const char **nargv = prepare_git_cmd(argv);
+ trace_argv_printf(nargv, "trace: exec:");
+
+ /* execvp() can only ever return if it fails */
+ execvp("git", (char **)nargv);
+
+ trace_printf("trace: exec failed: %s\n", strerror(errno));
+
+ free(nargv);
+ return -1;
}