Merge git://repo.or.cz/git-gui
[gitweb.git] / exec_cmd.c
index 9b74ed2f42ad9a452f5a1d9ab55d5ca1e1de2594..da04efe951e5b8dc4e7b72246212d8ebabc09ec6 100644 (file)
@@ -4,12 +4,45 @@
 #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;
 
-void git_set_exec_path(const char *exec_path)
+static const char *builtin_exec_path(void)
 {
-       current_exec_path = exec_path;
+#ifndef __MINGW32__
+       return GIT_EXEC_PATH;
+#else
+       int len;
+       char *p, *q, *sl;
+       static char *ep;
+       if (ep)
+               return ep;
+
+       len = strlen(_pgmptr);
+       if (len < 2)
+               return ep = ".";
+
+       p = ep = xmalloc(len+1);
+       q = _pgmptr;
+       sl = NULL;
+       /* copy program name, turn '\\' into '/', skip last part */
+       while ((*p = *q)) {
+               if (*q == '\\' || *q == '/') {
+                       *p = '/';
+                       sl = p;
+               }
+               p++, q++;
+       }
+       if (sl)
+               *sl = '\0';
+       else
+               ep[0] = '.', ep[1] = '\0';
+       return ep;
+#endif
+}
+
+void git_set_argv_exec_path(const char *exec_path)
+{
+       argv_exec_path = exec_path;
 }
 
 
@@ -18,96 +51,73 @@ const char *git_exec_path(void)
 {
        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 builtin_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_absolute_path(path));
+
+               strbuf_addch(out, PATH_SEP);
+       }
+}
 
-int execv_git_cmd(const char **argv)
+void setup_path(const char *cmd_path)
 {
-       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);
-               }
+       const char *old_path = getenv("PATH");
+       struct strbuf new_path;
 
-               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;
-               }
+       strbuf_init(&new_path, 0);
 
-               /* 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.
-                */
+       add_path(&new_path, argv_exec_path);
+       add_path(&new_path, getenv(EXEC_PATH_ENVIRONMENT));
+       add_path(&new_path, builtin_exec_path());
+       add_path(&new_path, cmd_path);
 
-               tmp = argv[0];
-               argv[0] = git_command;
+       if (old_path)
+               strbuf_addstr(&new_path, old_path);
+       else
+               strbuf_addstr(&new_path, "/usr/local/bin:/usr/bin:/bin");
 
-               trace_argv_printf(argv, -1, "trace: exec:");
+       setenv("PATH", new_path.buf, 1);
 
-               /* execve() can only ever return if it fails */
-               execve(git_command, (char **)argv, environ);
+       strbuf_release(&new_path);
+}
 
-               trace_printf("trace: exec failed: %s\n", strerror(errno));
+int execv_git_cmd(const char **argv)
+{
+       int argc;
+       const char **nargv;
 
-               argv[0] = tmp;
-       }
-       return -1;
+       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;
+       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;
 }