]> git.scripts.mit.edu Git - git.git/blobdiff - path.c
apply: reject input that touches outside the working area
[git.git] / path.c
diff --git a/path.c b/path.c
index 3d244d3e03ae5c3d5afe452f903ba204849ad03b..4ef1b01e05bc1b32d337d13a1b654e4993ea0456 100644 (file)
--- a/path.c
+++ b/path.c
@@ -434,6 +434,16 @@ int adjust_shared_perm(const char *path)
        return 0;
 }
 
+static int have_same_root(const char *path1, const char *path2)
+{
+       int is_abs1, is_abs2;
+
+       is_abs1 = is_absolute_path(path1);
+       is_abs2 = is_absolute_path(path2);
+       return (is_abs1 && is_abs2 && tolower(path1[0]) == tolower(path2[0])) ||
+              (!is_abs1 && !is_abs2);
+}
+
 /*
  * Give path as relative to prefix.
  *
@@ -454,6 +464,16 @@ const char *relative_path(const char *in, const char *prefix,
        else if (!prefix_len)
                return in;
 
+       if (have_same_root(in, prefix)) {
+               /* bypass dos_drive, for "c:" is identical to "C:" */
+               if (has_dos_drive_prefix(in)) {
+                       i = 2;
+                       j = 2;
+               }
+       } else {
+               return in;
+       }
+
        while (i < prefix_len && j < in_len && prefix[i] == in[j]) {
                if (is_dir_sep(prefix[i])) {
                        while (is_dir_sep(prefix[i]))
@@ -530,6 +550,51 @@ const char *relative_path(const char *in, const char *prefix,
        return sb->buf;
 }
 
+/*
+ * A simpler implementation of relative_path
+ *
+ * Get relative path by removing "prefix" from "in". This function
+ * first appears in v1.5.6-1-g044bbbc, and makes git_dir shorter
+ * to increase performance when traversing the path to work_tree.
+ */
+const char *remove_leading_path(const char *in, const char *prefix)
+{
+       static char buf[PATH_MAX + 1];
+       int i = 0, j = 0;
+
+       if (!prefix || !prefix[0])
+               return in;
+       while (prefix[i]) {
+               if (is_dir_sep(prefix[i])) {
+                       if (!is_dir_sep(in[j]))
+                               return in;
+                       while (is_dir_sep(prefix[i]))
+                               i++;
+                       while (is_dir_sep(in[j]))
+                               j++;
+                       continue;
+               } else if (in[j] != prefix[i]) {
+                       return in;
+               }
+               i++;
+               j++;
+       }
+       if (
+           /* "/foo" is a prefix of "/foo" */
+           in[j] &&
+           /* "/foo" is not a prefix of "/foobar" */
+           !is_dir_sep(prefix[i-1]) && !is_dir_sep(in[j])
+          )
+               return in;
+       while (is_dir_sep(in[j]))
+               j++;
+       if (!in[j])
+               strcpy(buf, ".");
+       else
+               strcpy(buf, in + j);
+       return buf;
+}
+
 /*
  * It is okay if dst == src, but they should not overlap otherwise.
  *
@@ -543,8 +608,14 @@ const char *relative_path(const char *in, const char *prefix,
  *
  * Note that this function is purely textual.  It does not follow symlinks,
  * verify the existence of the path, or make any system calls.
+ *
+ * prefix_len != NULL is for a specific case of prefix_pathspec():
+ * assume that src == dst and src[0..prefix_len-1] is already
+ * normalized, any time "../" eats up to the prefix_len part,
+ * prefix_len is reduced. In the end prefix_len is the remaining
+ * prefix that has not been overridden by user pathspec.
  */
-int normalize_path_copy(char *dst, const char *src)
+int normalize_path_copy_len(char *dst, const char *src, int *prefix_len)
 {
        char *dst0;
 
@@ -619,11 +690,18 @@ int normalize_path_copy(char *dst, const char *src)
                /* Windows: dst[-1] cannot be backslash anymore */
                while (dst0 < dst && dst[-1] != '/')
                        dst--;
+               if (prefix_len && *prefix_len > dst - dst0)
+                       *prefix_len = dst - dst0;
        }
        *dst = '\0';
        return 0;
 }
 
+int normalize_path_copy(char *dst, const char *src)
+{
+       return normalize_path_copy_len(dst, src, NULL);
+}
+
 /*
  * path = Canonical absolute path
  * prefixes = string_list containing normalized, absolute paths without
@@ -752,3 +830,36 @@ int offset_1st_component(const char *path)
                return 2 + is_dir_sep(path[2]);
        return is_dir_sep(path[0]);
 }
+
+static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
+{
+       if (len < skip)
+               return 0;
+       len -= skip;
+       path += skip;
+       while (len-- > 0) {
+               char c = *(path++);
+               if (c != ' ' && c != '.')
+                       return 0;
+       }
+       return 1;
+}
+
+int is_ntfs_dotgit(const char *name)
+{
+       int len;
+
+       for (len = 0; ; len++)
+               if (!name[len] || name[len] == '\\' || is_dir_sep(name[len])) {
+                       if (only_spaces_and_periods(name, len, 4) &&
+                                       !strncasecmp(name, ".git", 4))
+                               return 1;
+                       if (only_spaces_and_periods(name, len, 5) &&
+                                       !strncasecmp(name, "git~1", 5))
+                               return 1;
+                       if (name[len] != '\\')
+                               return 0;
+                       name += len + 1;
+                       len = -1;
+               }
+}