int git_attr_cache__get( git_attr_file **out, git_repository *repo, git_attr_session *attr_session, git_attr_file_source source, const char *base, const char *filename, git_attr_file_parser parser) { int error = 0; git_attr_cache *cache = git_repository_attr_cache(repo); git_attr_file_entry *entry = NULL; git_attr_file *file = NULL, *updated = NULL; if ((error = attr_cache_lookup( &file, &entry, repo, attr_session, source, base, filename)) < 0) return error; /* load file if we don't have one or if existing one is out of date */ if (!file || (error = git_attr_file__out_of_date(repo, attr_session, file)) > 0) error = git_attr_file__load(&updated, repo, attr_session, entry, source, parser); /* if we loaded the file, insert into and/or update cache */ if (updated) { if ((error = attr_cache_upsert(cache, updated)) < 0) git_attr_file__free(updated); else { git_attr_file__free(file); /* offset incref from lookup */ file = updated; } } /* if file could not be loaded */ if (error < 0) { /* remove existing entry */ if (file) { attr_cache_remove(cache, file); git_attr_file__free(file); /* offset incref from lookup */ file = NULL; } /* no error if file simply doesn't exist */ if (error == GIT_ENOTFOUND) { giterr_clear(); error = 0; } } *out = file; return error; }
void test_attr_file__simple_read(void) { git_attr_file *file; git_attr_assignment *assign; git_attr_rule *rule; cl_git_pass(git_attr_file__new(&file)); cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr0"), file)); cl_assert_strequal(cl_fixture("attr/attr0"), file->path); cl_assert(file->rules.length == 1); rule = get_rule(0); cl_assert(rule != NULL); cl_assert_strequal("*", rule->match.pattern); cl_assert(rule->match.length == 1); cl_assert(rule->match.flags == 0); cl_assert(rule->assigns.length == 1); assign = get_assign(rule, 0); cl_assert(assign != NULL); cl_assert_strequal("binary", assign->name); cl_assert(assign->value == GIT_ATTR_TRUE); cl_assert(!assign->is_allocated); git_attr_file__free(file); }
void test_attr_file__simple_read(void) { git_attr_file *file; git_attr_assignment *assign; git_attr_rule *rule; cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr0"))); cl_assert_equal_s(cl_fixture("attr/attr0"), file->key + 2); cl_assert(file->rules.length == 1); rule = get_rule(0); cl_assert(rule != NULL); cl_assert_equal_s("*", rule->match.pattern); cl_assert(rule->match.length == 1); cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0); cl_assert(rule->assigns.length == 1); assign = get_assign(rule, 0); cl_assert(assign != NULL); cl_assert_equal_s("binary", assign->name); cl_assert(GIT_ATTR_TRUE(assign->value)); git_attr_file__free(file); }
int git_attr_file__new( git_attr_file **attrs_ptr, git_attr_file_source from, const char *path, git_pool *pool) { git_attr_file *attrs = NULL; attrs = git__calloc(1, sizeof(git_attr_file)); GITERR_CHECK_ALLOC(attrs); if (pool) attrs->pool = pool; else { attrs->pool = git__calloc(1, sizeof(git_pool)); if (!attrs->pool || git_pool_init(attrs->pool, 1, 0) < 0) goto fail; attrs->pool_is_allocated = true; } if (path) { size_t len = strlen(path); attrs->key = git_pool_malloc(attrs->pool, (uint32_t)len + 3); GITERR_CHECK_ALLOC(attrs->key); attrs->key[0] = '0' + from; attrs->key[1] = '#'; memcpy(&attrs->key[2], path, len); attrs->key[len + 2] = '\0'; } if (git_vector_init(&attrs->rules, 4, NULL) < 0) goto fail; *attrs_ptr = attrs; return 0; fail: git_attr_file__free(attrs); attrs_ptr = NULL; return -1; }
void test_attr_file__check_attr_examples(void) { git_attr_file *file; git_attr_rule *rule; git_attr_assignment *assign; cl_git_pass(git_attr_file__new(&file)); cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr3"), file)); cl_assert_strequal(cl_fixture("attr/attr3"), file->path); cl_assert(file->rules.length == 3); rule = get_rule(0); cl_assert_strequal("*.java", rule->match.pattern); cl_assert(rule->assigns.length == 3); assign = git_attr_rule__lookup_assignment(rule, "diff"); cl_assert_strequal("diff", assign->name); cl_assert_strequal("java", assign->value); assign = git_attr_rule__lookup_assignment(rule, "crlf"); cl_assert_strequal("crlf", assign->name); cl_assert(GIT_ATTR_FALSE == assign->value); assign = git_attr_rule__lookup_assignment(rule, "myAttr"); cl_assert_strequal("myAttr", assign->name); cl_assert(GIT_ATTR_TRUE == assign->value); assign = git_attr_rule__lookup_assignment(rule, "missing"); cl_assert(assign == NULL); rule = get_rule(1); cl_assert_strequal("NoMyAttr.java", rule->match.pattern); cl_assert(rule->assigns.length == 1); assign = get_assign(rule, 0); cl_assert_strequal("myAttr", assign->name); cl_assert(assign->value == NULL); rule = get_rule(2); cl_assert_strequal("README", rule->match.pattern); cl_assert(rule->assigns.length == 1); assign = get_assign(rule, 0); cl_assert_strequal("caveat", assign->name); cl_assert_strequal("unspecified", assign->value); git_attr_file__free(file); }
static void attr_cache__free(git_attr_cache *cache) { bool unlock; if (!cache) return; unlock = (git_mutex_lock(&cache->lock) == 0); if (cache->files != NULL) { git_attr_file_entry *entry; git_attr_file *file; int i; git_strmap_foreach_value(cache->files, entry, { for (i = 0; i < GIT_ATTR_FILE_NUM_SOURCES; ++i) { if ((file = git__swap(entry->file[i], NULL)) != NULL) { GIT_REFCOUNT_OWN(file, NULL); git_attr_file__free(file); } } });
static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file) { int error = 0; git_attr_file_entry *entry; if (!file) return 0; if ((error = attr_cache_lock(cache)) < 0) return error; if ((entry = attr_cache_lookup_entry(cache, file->entry->path)) != NULL) file = git__compare_and_swap(&entry->file[file->source], file, NULL); attr_cache_unlock(cache); if (file) { GIT_REFCOUNT_OWN(file, NULL); git_attr_file__free(file); } return error; }
int git_attr_file__new_and_load( git_attr_file **attrs_ptr, const char *path) { int error; git_buf content = GIT_BUF_INIT; if ((error = git_attr_file__new(attrs_ptr, 0, path, NULL)) < 0) return error; if (!(error = git_futils_readbuffer(&content, path))) error = git_attr_file__parse_buffer( NULL, git_buf_cstr(&content), *attrs_ptr); git_buf_free(&content); if (error) { git_attr_file__free(*attrs_ptr); *attrs_ptr = NULL; } return error; }
/* insert entry or replace existing if we raced with another thread */ static int attr_cache_upsert(git_attr_cache *cache, git_attr_file *file) { git_attr_file_entry *entry; git_attr_file *old; if (attr_cache_lock(cache) < 0) return -1; entry = attr_cache_lookup_entry(cache, file->entry->path); GIT_REFCOUNT_OWN(file, entry); GIT_REFCOUNT_INC(file); old = git__compare_and_swap( &entry->file[file->source], entry->file[file->source], file); if (old) { GIT_REFCOUNT_OWN(old, NULL); git_attr_file__free(old); } attr_cache_unlock(cache); return 0; }
void test_attr_file__match_variants(void) { git_attr_file *file; git_attr_rule *rule; git_attr_assignment *assign; cl_git_pass(git_attr_file__new(&file)); cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr1"), file)); cl_assert_strequal(cl_fixture("attr/attr1"), file->path); cl_assert(file->rules.length == 10); /* let's do a thorough check of this rule, then just verify * the things that are unique for the later rules */ rule = get_rule(0); cl_assert(rule); cl_assert_strequal("pat0", rule->match.pattern); cl_assert(rule->match.length == strlen("pat0")); cl_assert(rule->match.flags == 0); cl_assert(rule->assigns.length == 1); assign = get_assign(rule,0); cl_assert_strequal("attr0", assign->name); cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name)); cl_assert(assign->value == GIT_ATTR_TRUE); cl_assert(!assign->is_allocated); rule = get_rule(1); cl_assert_strequal("pat1", rule->match.pattern); cl_assert(rule->match.length == strlen("pat1")); cl_assert(rule->match.flags == GIT_ATTR_FNMATCH_NEGATIVE); rule = get_rule(2); cl_assert_strequal("pat2", rule->match.pattern); cl_assert(rule->match.length == strlen("pat2")); cl_assert(rule->match.flags == GIT_ATTR_FNMATCH_DIRECTORY); rule = get_rule(3); cl_assert_strequal("pat3dir/pat3file", rule->match.pattern); cl_assert(rule->match.flags == GIT_ATTR_FNMATCH_FULLPATH); rule = get_rule(4); cl_assert_strequal("pat4.*", rule->match.pattern); cl_assert(rule->match.flags == 0); rule = get_rule(5); cl_assert_strequal("*.pat5", rule->match.pattern); rule = get_rule(7); cl_assert_strequal("pat7[a-e]??[xyz]", rule->match.pattern); cl_assert(rule->assigns.length == 1); assign = get_assign(rule,0); cl_assert_strequal("attr7", assign->name); cl_assert(assign->value == GIT_ATTR_TRUE); rule = get_rule(8); cl_assert_strequal("pat8 with spaces", rule->match.pattern); cl_assert(rule->match.length == strlen("pat8 with spaces")); cl_assert(rule->match.flags == 0); rule = get_rule(9); cl_assert_strequal("pat9", rule->match.pattern); git_attr_file__free(file); }
void test_attr_file__assign_variants(void) { git_attr_file *file; git_attr_rule *rule; git_attr_assignment *assign; cl_git_pass(git_attr_file__new(&file)); cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr2"), file)); cl_assert_strequal(cl_fixture("attr/attr2"), file->path); cl_assert(file->rules.length == 11); check_one_assign(file, 0, 0, "pat0", "simple", GIT_ATTR_TRUE, 0); check_one_assign(file, 1, 0, "pat1", "neg", GIT_ATTR_FALSE, 0); check_one_assign(file, 2, 0, "*", "notundef", GIT_ATTR_TRUE, 0); check_one_assign(file, 3, 0, "pat2", "notundef", NULL, 0); check_one_assign(file, 4, 0, "pat3", "assigned", "test-value", 1); check_one_assign(file, 5, 0, "pat4", "rule-with-more-chars", "value-with-more-chars", 1); check_one_assign(file, 6, 0, "pat5", "empty", GIT_ATTR_TRUE, 0); check_one_assign(file, 7, 0, "pat6", "negempty", GIT_ATTR_FALSE, 0); rule = get_rule(8); cl_assert_strequal("pat7", rule->match.pattern); cl_assert(rule->assigns.length == 5); /* assignments will be sorted by hash value, so we have to do * lookups by search instead of by position */ assign = git_attr_rule__lookup_assignment(rule, "multiple"); cl_assert(assign); cl_assert_strequal("multiple", assign->name); cl_assert(assign->value == GIT_ATTR_TRUE); assign = git_attr_rule__lookup_assignment(rule, "single"); cl_assert(assign); cl_assert_strequal("single", assign->name); cl_assert(assign->value == GIT_ATTR_FALSE); assign = git_attr_rule__lookup_assignment(rule, "values"); cl_assert(assign); cl_assert_strequal("values", assign->name); cl_assert_strequal("1", assign->value); assign = git_attr_rule__lookup_assignment(rule, "also"); cl_assert(assign); cl_assert_strequal("also", assign->name); cl_assert_strequal("a-really-long-value/*", assign->value); assign = git_attr_rule__lookup_assignment(rule, "happy"); cl_assert(assign); cl_assert_strequal("happy", assign->name); cl_assert_strequal("yes!", assign->value); assign = git_attr_rule__lookup_assignment(rule, "other"); cl_assert(!assign); rule = get_rule(9); cl_assert_strequal("pat8", rule->match.pattern); cl_assert(rule->assigns.length == 2); assign = git_attr_rule__lookup_assignment(rule, "again"); cl_assert(assign); cl_assert_strequal("again", assign->name); cl_assert(assign->value == GIT_ATTR_TRUE); assign = git_attr_rule__lookup_assignment(rule, "another"); cl_assert(assign); cl_assert_strequal("another", assign->name); cl_assert_strequal("12321", assign->value); check_one_assign(file, 10, 0, "pat9", "at-eof", GIT_ATTR_FALSE, 0); git_attr_file__free(file); }
int git_attr_file__load( git_attr_file **out, git_repository *repo, git_attr_file_entry *entry, git_attr_file_source source, git_attr_file_parser parser) { int error = 0; git_blob *blob = NULL; git_buf content = GIT_BUF_INIT; const char *data = NULL; git_attr_file *file; struct stat st; *out = NULL; switch (source) { case GIT_ATTR_FILE__IN_MEMORY: /* in-memory attribute file doesn't need data */ break; case GIT_ATTR_FILE__FROM_INDEX: { git_oid id; if ((error = attr_file_oid_from_index(&id, repo, entry->path)) < 0 || (error = git_blob_lookup(&blob, repo, &id)) < 0) return error; data = git_blob_rawcontent(blob); break; } case GIT_ATTR_FILE__FROM_FILE: { int fd; if (p_stat(entry->fullpath, &st) < 0) return git_path_set_error(errno, entry->fullpath, "stat"); if (S_ISDIR(st.st_mode)) return GIT_ENOTFOUND; /* For open or read errors, return ENOTFOUND to skip item */ /* TODO: issue warning when warning API is available */ if ((fd = git_futils_open_ro(entry->fullpath)) < 0) return GIT_ENOTFOUND; error = git_futils_readbuffer_fd(&content, fd, (size_t)st.st_size); p_close(fd); if (error < 0) return GIT_ENOTFOUND; data = content.ptr; break; } default: giterr_set(GITERR_INVALID, "Unknown file source %d", source); return -1; } if ((error = git_attr_file__new(&file, entry, source)) < 0) goto cleanup; if (parser && (error = parser(repo, file, data)) < 0) { git_attr_file__free(file); goto cleanup; } /* write cache breaker */ if (source == GIT_ATTR_FILE__FROM_INDEX) git_oid_cpy(&file->cache_data.oid, git_blob_id(blob)); else if (source == GIT_ATTR_FILE__FROM_FILE) git_futils_filestamp_set_from_stat(&file->cache_data.stamp, &st); /* else always cacheable */ *out = file; cleanup: git_blob_free(blob); git_buf_free(&content); return error; }
int git_attr_file__load( git_attr_file **out, git_repository *repo, git_attr_session *attr_session, git_attr_file_entry *entry, git_attr_file_source source, git_attr_file_parser parser) { int error = 0; git_blob *blob = NULL; git_buf content = GIT_BUF_INIT; git_attr_file *file; struct stat st; bool nonexistent = false; *out = NULL; switch (source) { case GIT_ATTR_FILE__IN_MEMORY: /* in-memory attribute file doesn't need data */ break; case GIT_ATTR_FILE__FROM_INDEX: { git_oid id; if ((error = attr_file_oid_from_index(&id, repo, entry->path)) < 0 || (error = git_blob_lookup(&blob, repo, &id)) < 0) return error; /* Do not assume that data straight from the ODB is NULL-terminated; * copy the contents of a file to a buffer to work on */ git_buf_put(&content, git_blob_rawcontent(blob), git_blob_rawsize(blob)); break; } case GIT_ATTR_FILE__FROM_FILE: { int fd = -1; /* For open or read errors, pretend that we got ENOTFOUND. */ /* TODO: issue warning when warning API is available */ if (p_stat(entry->fullpath, &st) < 0 || S_ISDIR(st.st_mode) || (fd = git_futils_open_ro(entry->fullpath)) < 0 || (error = git_futils_readbuffer_fd(&content, fd, (size_t)st.st_size)) < 0) nonexistent = true; if (fd >= 0) p_close(fd); break; } default: giterr_set(GITERR_INVALID, "Unknown file source %d", source); return -1; } if ((error = git_attr_file__new(&file, entry, source)) < 0) goto cleanup; /* store the key of the attr_reader; don't bother with cache * invalidation during the same attr reader session. */ if (attr_session) file->session_key = attr_session->key; if (parser && (error = parser(repo, file, git_buf_cstr(&content))) < 0) { git_attr_file__free(file); goto cleanup; } /* write cache breakers */ if (nonexistent) file->nonexistent = 1; else if (source == GIT_ATTR_FILE__FROM_INDEX) git_oid_cpy(&file->cache_data.oid, git_blob_id(blob)); else if (source == GIT_ATTR_FILE__FROM_FILE) git_futils_filestamp_set_from_stat(&file->cache_data.stamp, &st); /* else always cacheable */ *out = file; cleanup: git_blob_free(blob); git_buf_free(&content); return error; }