parseopt: prevent KEEP_UNKNOWN and STOP_AT_NON_OPTION from being used together
[gitweb.git] / exec_cmd.c
index 33b17a6b45699e73a9b58f0ff02135eae913b47d..217c12577f52b8ff9d535a086ec75d54107ee01c 100644 (file)
@@ -4,8 +4,59 @@
 #define MAX_ARGS       32
 
 extern char **environ;
-static const char *builtin_exec_path = GIT_EXEC_PATH;
 static const char *argv_exec_path;
+static const char *argv0_path;
+
+const char *system_path(const char *path)
+{
+#ifdef RUNTIME_PREFIX
+       static const char *prefix;
+#else
+       static const char *prefix = PREFIX;
+#endif
+       struct strbuf d = STRBUF_INIT;
+
+       if (is_absolute_path(path))
+               return path;
+
+#ifdef RUNTIME_PREFIX
+       assert(argv0_path);
+       assert(is_absolute_path(argv0_path));
+
+       if (!prefix &&
+           !(prefix = strip_path_suffix(argv0_path, GIT_EXEC_PATH)) &&
+           !(prefix = strip_path_suffix(argv0_path, BINDIR)) &&
+           !(prefix = strip_path_suffix(argv0_path, "git"))) {
+               prefix = PREFIX;
+               fprintf(stderr, "RUNTIME_PREFIX requested, "
+                               "but prefix computation failed.  "
+                               "Using static fallback '%s'.\n", prefix);
+       }
+#endif
+
+       strbuf_addf(&d, "%s/%s", prefix, path);
+       path = strbuf_detach(&d, NULL);
+       return path;
+}
+
+const char *git_extract_argv0_path(const char *argv0)
+{
+       const char *slash;
+
+       if (!argv0 || !*argv0)
+               return NULL;
+       slash = argv0 + strlen(argv0);
+
+       while (argv0 <= slash && !is_dir_sep(*slash))
+               slash--;
+
+       if (slash >= argv0) {
+               argv0_path = xstrndup(argv0, slash - argv0);
+               return slash + 1;
+       }
+
+       return argv0;
+}
 
 void git_set_argv_exec_path(const char *exec_path)
 {
@@ -26,88 +77,66 @@ const char *git_exec_path(void)
                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));
 
-int execv_git_cmd(const char **argv)
+               strbuf_addch(out, PATH_SEP);
+       }
+}
+
+void setup_path(void)
 {
-       char git_command[PATH_MAX + 1];
-       int i;
-       const char *paths[] = { argv_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 = STRBUF_INIT;
 
-               /* 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, git_exec_path());
+       add_path(&new_path, argv0_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));
+const char **prepare_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;
+       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;
 }