]> git.proxmox.com Git - libgit2.git/blobdiff - src/attr_file.c
Fix core.excludesfile named .gitignore
[libgit2.git] / src / attr_file.c
index 628cb1544b93a38701b34babb5839023c4587dd5..ea92336f7559e22f1bfbcdcd2e2d9c70fc2c8ff6 100644 (file)
@@ -1,12 +1,17 @@
 #include "common.h"
 #include "repository.h"
 #include "filebuf.h"
+#include "attr.h"
 #include "git2/blob.h"
 #include "git2/tree.h"
 #include <ctype.h>
 
 static int sort_by_hash_and_name(const void *a_raw, const void *b_raw);
 static void git_attr_rule__clear(git_attr_rule *rule);
+static bool parse_optimized_patterns(
+       git_attr_fnmatch *spec,
+       git_pool *pool,
+       const char *pattern);
 
 int git_attr_file__new(
        git_attr_file **attrs_ptr,
@@ -34,7 +39,7 @@ int git_attr_file__new(
                attrs->key = git_pool_malloc(attrs->pool, (uint32_t)len + 3);
                GITERR_CHECK_ALLOC(attrs->key);
 
-               attrs->key[0] = '0' + from;
+               attrs->key[0] = '0' + (char)from;
                attrs->key[1] = '#';
                memcpy(&attrs->key[2], path, len);
                attrs->key[len + 2] = '\0';
@@ -56,8 +61,7 @@ int git_attr_file__parse_buffer(
        git_repository *repo, void *parsedata, const char *buffer, git_attr_file *attrs)
 {
        int error = 0;
-       const char *scan = NULL;
-       char *context = NULL;
+       const char *scan = NULL, *context = NULL;
        git_attr_rule *rule = NULL;
 
        GIT_UNUSED(parsedata);
@@ -67,16 +71,20 @@ int git_attr_file__parse_buffer(
        scan = buffer;
 
        /* if subdir file path, convert context for file paths */
-       if (attrs->key && git__suffixcmp(attrs->key, "/" GIT_ATTR_FILE) == 0) {
+       if (attrs->key &&
+               git_path_root(attrs->key + 2) < 0 &&
+               git__suffixcmp(attrs->key, "/" GIT_ATTR_FILE) == 0)
                context = attrs->key + 2;
-               context[strlen(context) - strlen(GIT_ATTR_FILE)] = '\0';
-       }
 
        while (!error && *scan) {
                /* allocate rule if needed */
-               if (!rule && !(rule = git__calloc(1, sizeof(git_attr_rule)))) {
-                       error = -1;
-                       break;
+               if (!rule) {
+                       if (!(rule = git__calloc(1, sizeof(git_attr_rule)))) {
+                               error = -1;
+                               break;
+                       }
+                       rule->match.flags = GIT_ATTR_FNMATCH_ALLOWNEG |
+                               GIT_ATTR_FNMATCH_ALLOWMACRO;
                }
 
                /* parse the next "pattern attr attr attr" line */
@@ -106,10 +114,6 @@ int git_attr_file__parse_buffer(
 
        git_attr_rule__free(rule);
 
-       /* restore file path used for context */
-       if (context)
-               context[strlen(context)] = '.'; /* first char of GIT_ATTR_FILE */
-
        return error;
 }
 
@@ -295,7 +299,6 @@ void git_attr_path__free(git_attr_path *info)
        info->basename = NULL;
 }
 
-
 /*
  * From gitattributes(5):
  *
@@ -344,8 +347,11 @@ int git_attr_fnmatch__parse(
 
        assert(spec && base && *base);
 
-       spec->flags = (spec->flags & GIT_ATTR_FNMATCH_ALLOWSPACE);
-       allow_space = (spec->flags != 0);
+       if (parse_optimized_patterns(spec, pool, *base))
+               return 0;
+
+       spec->flags = (spec->flags & GIT_ATTR_FNMATCH__INCOMING);
+       allow_space = ((spec->flags & GIT_ATTR_FNMATCH_ALLOWSPACE) != 0);
 
        pattern = *base;
 
@@ -355,7 +361,7 @@ int git_attr_fnmatch__parse(
                return GIT_ENOTFOUND;
        }
 
-       if (*pattern == '[') {
+       if (*pattern == '[' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWMACRO) != 0) {
                if (strncmp(pattern, "[attr]", 6) == 0) {
                        spec->flags = spec->flags | GIT_ATTR_FNMATCH_MACRO;
                        pattern += 6;
@@ -363,7 +369,7 @@ int git_attr_fnmatch__parse(
                /* else a character range like [a-e]* which is accepted */
        }
 
-       if (*pattern == '!') {
+       if (*pattern == '!' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWNEG) != 0) {
                spec->flags = spec->flags | GIT_ATTR_FNMATCH_NEGATIVE;
                pattern++;
        }
@@ -390,7 +396,8 @@ int git_attr_fnmatch__parse(
 
        *base = scan;
 
-       spec->length = scan - pattern;
+       if ((spec->length = scan - pattern) == 0)
+               return GIT_ENOTFOUND;
 
        if (pattern[spec->length - 1] == '/') {
                spec->length--;
@@ -402,7 +409,10 @@ int git_attr_fnmatch__parse(
        if ((spec->flags & GIT_ATTR_FNMATCH_FULLPATH) != 0 &&
                source != NULL && git_path_root(pattern) < 0)
        {
-               size_t sourcelen = strlen(source);
+        /* use context path minus the trailing filename */
+        char *slash = strrchr(source, '/');
+        size_t sourcelen = slash ? slash - source + 1 : 0;
+
                /* given an unrooted fullpath match from a file inside a repo,
                 * prefix the pattern with the relative directory of the source file
                 */
@@ -429,6 +439,22 @@ int git_attr_fnmatch__parse(
        return 0;
 }
 
+static bool parse_optimized_patterns(
+       git_attr_fnmatch *spec,
+       git_pool *pool,
+       const char *pattern)
+{
+       if (!pattern[1] && (pattern[0] == '*' || pattern[0] == '.')) {
+               spec->flags = GIT_ATTR_FNMATCH_MATCH_ALL;
+               spec->pattern = git_pool_strndup(pool, pattern, 1);
+               spec->length = 1;
+
+               return true;
+       }
+
+       return false;
+}
+
 static int sort_by_hash_and_name(const void *a_raw, const void *b_raw)
 {
        const git_attr_name *a = a_raw;
@@ -474,7 +500,7 @@ int git_attr_assignment__parse(
 
        assert(assigns && !assigns->length);
 
-       assigns->_cmp = sort_by_hash_and_name;
+       git_vector_set_cmp(assigns, sort_by_hash_and_name);
 
        while (*scan && *scan != '\n') {
                const char *name_start, *value_start;