Sync with maint
[gitweb.git] / setup.c
diff --git a/setup.c b/setup.c
index 3b111ea7cf5f68ecca085f5a56421ea190e1771a..6c2deda18492acb5a8597563d6843f9d0dd232c0 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -4,89 +4,6 @@
 static int inside_git_dir = -1;
 static int inside_work_tree = -1;
 
-static int sanitary_path_copy(char *dst, const char *src)
-{
-       char *dst0 = dst;
-
-       if (*src == '/') {
-               *dst++ = '/';
-               while (*src == '/')
-                       src++;
-       }
-
-       for (;;) {
-               char c = *src;
-
-               /*
-                * A path component that begins with . could be
-                * special:
-                * (1) "." and ends   -- ignore and terminate.
-                * (2) "./"           -- ignore them, eat slash and continue.
-                * (3) ".." and ends  -- strip one and terminate.
-                * (4) "../"          -- strip one, eat slash and continue.
-                */
-               if (c == '.') {
-                       switch (src[1]) {
-                       case '\0':
-                               /* (1) */
-                               src++;
-                               break;
-                       case '/':
-                               /* (2) */
-                               src += 2;
-                               while (*src == '/')
-                                       src++;
-                               continue;
-                       case '.':
-                               switch (src[2]) {
-                               case '\0':
-                                       /* (3) */
-                                       src += 2;
-                                       goto up_one;
-                               case '/':
-                                       /* (4) */
-                                       src += 3;
-                                       while (*src == '/')
-                                               src++;
-                                       goto up_one;
-                               }
-                       }
-               }
-
-               /* copy up to the next '/', and eat all '/' */
-               while ((c = *src++) != '\0' && c != '/')
-                       *dst++ = c;
-               if (c == '/') {
-                       *dst++ = c;
-                       while (c == '/')
-                               c = *src++;
-                       src--;
-               } else if (!c)
-                       break;
-               continue;
-
-       up_one:
-               /*
-                * dst0..dst is prefix portion, and dst[-1] is '/';
-                * go up one level.
-                */
-               dst -= 2; /* go past trailing '/' if any */
-               if (dst < dst0)
-                       return -1;
-               while (1) {
-                       if (dst <= dst0)
-                               break;
-                       c = *dst--;
-                       if (c == '/') {
-                               dst += 2;
-                               break;
-                       }
-               }
-       }
-       *dst = '\0';
-       return 0;
-}
-
 const char *prefix_path(const char *prefix, int len, const char *path)
 {
        const char *orig = path;
@@ -98,7 +15,7 @@ const char *prefix_path(const char *prefix, int len, const char *path)
                        memcpy(sanitized, prefix, len);
                strcpy(sanitized + len, path);
        }
-       if (sanitary_path_copy(sanitized, sanitized))
+       if (normalize_path_copy(sanitized, sanitized))
                goto error_out;
        if (is_absolute_path(orig)) {
                const char *work_tree = get_git_work_tree();
@@ -107,9 +24,7 @@ const char *prefix_path(const char *prefix, int len, const char *path)
                if (strncmp(sanitized, work_tree, len) ||
                    (sanitized[len] != '\0' && sanitized[len] != '/')) {
                error_out:
-                       error("'%s' is outside repository", orig);
-                       free(sanitized);
-                       return NULL;
+                       die("'%s' is outside repository", orig);
                }
                if (sanitized[len] == '/')
                        len++;
@@ -126,10 +41,23 @@ const char *prefix_path(const char *prefix, int len, const char *path)
 const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
 {
        static char path[PATH_MAX];
+#ifndef __MINGW32__
        if (!pfx || !*pfx || is_absolute_path(arg))
                return arg;
        memcpy(path, pfx, pfx_len);
        strcpy(path + pfx_len, arg);
+#else
+       char *p;
+       /* don't add prefix to absolute paths, but still replace '\' by '/' */
+       if (is_absolute_path(arg))
+               pfx_len = 0;
+       else
+               memcpy(path, pfx, pfx_len);
+       strcpy(path + pfx_len, arg);
+       for (p = path + pfx_len; *p; p++)
+               if (*p == '\\')
+                       *p = '/';
+#endif
        return path;
 }
 
@@ -200,10 +128,7 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
        prefixlen = prefix ? strlen(prefix) : 0;
        while (*src) {
                const char *p = prefix_path(prefix, prefixlen, *src);
-               if (p)
-                       *(dst++) = p;
-               else
-                       exit(128); /* error message already given */
+               *(dst++) = p;
                src++;
        }
        *dst = NULL;
@@ -360,10 +285,11 @@ const char *read_gitfile_gently(const char *path)
 const char *setup_git_directory_gently(int *nongit_ok)
 {
        const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
+       const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
        static char cwd[PATH_MAX+1];
        const char *gitdirenv;
        const char *gitfile_dir;
-       int len, offset;
+       int len, offset, ceil_offset;
 
        /*
         * Let's assume that we are in a git repository.
@@ -415,6 +341,10 @@ const char *setup_git_directory_gently(int *nongit_ok)
        if (!getcwd(cwd, sizeof(cwd)-1))
                die("Unable to read current working directory");
 
+       ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs);
+       if (ceil_offset < 0 && has_dos_drive_prefix(cwd))
+               ceil_offset = 1;
+
        /*
         * Test in the following order (relative to the cwd):
         * - .git (file containing "gitdir: <path>")
@@ -440,22 +370,26 @@ const char *setup_git_directory_gently(int *nongit_ok)
                        inside_git_dir = 1;
                        if (!work_tree_env)
                                inside_work_tree = 0;
-                       setenv(GIT_DIR_ENVIRONMENT, ".", 1);
+                       if (offset != len) {
+                               cwd[offset] = '\0';
+                               setenv(GIT_DIR_ENVIRONMENT, cwd, 1);
+                       } else
+                               setenv(GIT_DIR_ENVIRONMENT, ".", 1);
                        check_repository_format_gently(nongit_ok);
                        return NULL;
                }
-               chdir("..");
-               do {
-                       if (!offset) {
-                               if (nongit_ok) {
-                                       if (chdir(cwd))
-                                               die("Cannot come back to cwd");
-                                       *nongit_ok = 1;
-                                       return NULL;
-                               }
-                               die("Not a git repository");
+               while (--offset > ceil_offset && cwd[offset] != '/');
+               if (offset <= ceil_offset) {
+                       if (nongit_ok) {
+                               if (chdir(cwd))
+                                       die("Cannot come back to cwd");
+                               *nongit_ok = 1;
+                               return NULL;
                        }
-               } while (cwd[--offset] != '/');
+                       die("Not a git repository (or any of the parent directories): %s", DEFAULT_GIT_DIR_ENVIRONMENT);
+               }
+               if (chdir(".."))
+                       die("Cannot change to %s/..: %s", cwd, strerror(errno));
        }
 
        inside_git_dir = 0;
@@ -561,6 +495,8 @@ const char *setup_git_directory(void)
                if (retval && chdir(retval))
                        die ("Could not jump back into original cwd");
                rel = get_relative_cwd(buffer, PATH_MAX, get_git_work_tree());
+               if (rel && *rel && chdir(get_git_work_tree()))
+                       die ("Could not jump to working directory");
                return rel && *rel ? strcat(rel, "/") : NULL;
        }