Sync with Git 2.13.7
[gitweb.git] / path.c
diff --git a/path.c b/path.c
index e50d2befcf826680c70ac59295c06004dd6221cf..9ac0531a29b94e7d83c52c172d566a0e243e0025 100644 (file)
--- a/path.c
+++ b/path.c
@@ -33,11 +33,10 @@ static struct strbuf *get_pathname(void)
        return sb;
 }
 
-static char *cleanup_path(char *path)
+static const char *cleanup_path(const char *path)
 {
        /* Clean it up */
-       if (!memcmp(path, "./", 2)) {
-               path += 2;
+       if (skip_prefix(path, "./", &path)) {
                while (*path == '/')
                        path++;
        }
@@ -46,7 +45,7 @@ static char *cleanup_path(char *path)
 
 static void strbuf_cleanup_path(struct strbuf *sb)
 {
-       char *path = cleanup_path(sb->buf);
+       const char *path = cleanup_path(sb->buf);
        if (path > sb->buf)
                strbuf_remove(sb, 0, path - sb->buf);
 }
@@ -63,7 +62,7 @@ char *mksnpath(char *buf, size_t n, const char *fmt, ...)
                strlcpy(buf, bad_path, n);
                return buf;
        }
-       return cleanup_path(buf);
+       return (char *)cleanup_path(buf);
 }
 
 static int dir_prefix(const char *buf, const char *dir)
@@ -636,8 +635,9 @@ void strbuf_git_common_path(struct strbuf *sb,
 int validate_headref(const char *path)
 {
        struct stat st;
-       char *buf, buffer[256];
-       unsigned char sha1[20];
+       char buffer[256];
+       const char *refname;
+       struct object_id oid;
        int fd;
        ssize_t len;
 
@@ -661,24 +661,24 @@ int validate_headref(const char *path)
        len = read_in_full(fd, buffer, sizeof(buffer)-1);
        close(fd);
 
+       if (len < 0)
+               return -1;
+       buffer[len] = '\0';
+
        /*
         * Is it a symbolic ref?
         */
-       if (len < 4)
-               return -1;
-       if (!memcmp("ref:", buffer, 4)) {
-               buf = buffer + 4;
-               len -= 4;
-               while (len && isspace(*buf))
-                       buf++, len--;
-               if (len >= 5 && !memcmp("refs/", buf, 5))
+       if (skip_prefix(buffer, "ref:", &refname)) {
+               while (isspace(*refname))
+                       refname++;
+               if (starts_with(refname, "refs/"))
                        return 0;
        }
 
        /*
         * Is this a detached HEAD?
         */
-       if (!get_sha1_hex(buffer, sha1))
+       if (!get_oid_hex(buffer, &oid))
                return 0;
 
        return -1;
@@ -1304,7 +1304,7 @@ static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
 
 int is_ntfs_dotgit(const char *name)
 {
-       int len;
+       size_t len;
 
        for (len = 0; ; len++)
                if (!name[len] || name[len] == '\\' || is_dir_sep(name[len])) {
@@ -1321,6 +1321,90 @@ int is_ntfs_dotgit(const char *name)
                }
 }
 
+static int is_ntfs_dot_generic(const char *name,
+                              const char *dotgit_name,
+                              size_t len,
+                              const char *dotgit_ntfs_shortname_prefix)
+{
+       int saw_tilde;
+       size_t i;
+
+       if ((name[0] == '.' && !strncasecmp(name + 1, dotgit_name, len))) {
+               i = len + 1;
+only_spaces_and_periods:
+               for (;;) {
+                       char c = name[i++];
+                       if (!c)
+                               return 1;
+                       if (c != ' ' && c != '.')
+                               return 0;
+               }
+       }
+
+       /*
+        * Is it a regular NTFS short name, i.e. shortened to 6 characters,
+        * followed by ~1, ... ~4?
+        */
+       if (!strncasecmp(name, dotgit_name, 6) && name[6] == '~' &&
+           name[7] >= '1' && name[7] <= '4') {
+               i = 8;
+               goto only_spaces_and_periods;
+       }
+
+       /*
+        * Is it a fall-back NTFS short name (for details, see
+        * https://en.wikipedia.org/wiki/8.3_filename?
+        */
+       for (i = 0, saw_tilde = 0; i < 8; i++)
+               if (name[i] == '\0')
+                       return 0;
+               else if (saw_tilde) {
+                       if (name[i] < '0' || name[i] > '9')
+                               return 0;
+               } else if (name[i] == '~') {
+                       if (name[++i] < '1' || name[i] > '9')
+                               return 0;
+                       saw_tilde = 1;
+               } else if (i >= 6)
+                       return 0;
+               else if (name[i] < 0) {
+                       /*
+                        * We know our needles contain only ASCII, so we clamp
+                        * here to make the results of tolower() sane.
+                        */
+                       return 0;
+               } else if (tolower(name[i]) != dotgit_ntfs_shortname_prefix[i])
+                       return 0;
+
+       goto only_spaces_and_periods;
+}
+
+/*
+ * Inline helper to make sure compiler resolves strlen() on literals at
+ * compile time.
+ */
+static inline int is_ntfs_dot_str(const char *name, const char *dotgit_name,
+                                 const char *dotgit_ntfs_shortname_prefix)
+{
+       return is_ntfs_dot_generic(name, dotgit_name, strlen(dotgit_name),
+                                  dotgit_ntfs_shortname_prefix);
+}
+
+int is_ntfs_dotgitmodules(const char *name)
+{
+       return is_ntfs_dot_str(name, "gitmodules", "gi7eba");
+}
+
+int is_ntfs_dotgitignore(const char *name)
+{
+       return is_ntfs_dot_str(name, "gitignore", "gi250a");
+}
+
+int is_ntfs_dotgitattributes(const char *name)
+{
+       return is_ntfs_dot_str(name, "gitattributes", "gi7d29");
+}
+
 int looks_like_command_line_option(const char *str)
 {
        return str && str[0] == '-';