]>
git.proxmox.com Git - libgit2.git/blob - src/attr.c
1 #include "repository.h"
6 static int collect_attr_files(
7 git_repository
*repo
, const char *path
, git_vector
*files
);
11 git_repository
*repo
, const char *pathname
,
12 const char *name
, const char **value
)
16 git_vector files
= GIT_VECTOR_INIT
;
24 if ((error
= git_attr_path__init(
25 &path
, pathname
, git_repository_workdir(repo
))) < GIT_SUCCESS
||
26 (error
= collect_attr_files(repo
, pathname
, &files
)) < GIT_SUCCESS
)
27 return git__rethrow(error
, "Could not get attribute for %s", pathname
);
30 attr
.name_hash
= git_attr_file__name_hash(name
);
32 git_vector_foreach(&files
, i
, file
) {
34 git_attr_file__foreach_matching_rule(file
, &path
, j
, rule
) {
35 int pos
= git_vector_bsearch(&rule
->assigns
, &attr
);
36 git_clearerror(); /* okay if search failed */
39 *value
= ((git_attr_assignment
*)git_vector_get(
40 &rule
->assigns
, pos
))->value
;
47 git_vector_free(&files
);
55 git_attr_assignment
*found
;
58 int git_attr_get_many(
59 git_repository
*repo
, const char *pathname
,
60 size_t num_attr
, const char **names
, const char **values
)
64 git_vector files
= GIT_VECTOR_INIT
;
68 attr_get_many_info
*info
= NULL
;
71 memset((void *)values
, 0, sizeof(const char *) * num_attr
);
73 if ((error
= git_attr_path__init(
74 &path
, pathname
, git_repository_workdir(repo
))) < GIT_SUCCESS
||
75 (error
= collect_attr_files(repo
, pathname
, &files
)) < GIT_SUCCESS
)
76 return git__rethrow(error
, "Could not get attributes for %s", pathname
);
78 if ((info
= git__calloc(num_attr
, sizeof(attr_get_many_info
))) == NULL
) {
79 git__rethrow(GIT_ENOMEM
, "Could not get attributes for %s", pathname
);
83 git_vector_foreach(&files
, i
, file
) {
85 git_attr_file__foreach_matching_rule(file
, &path
, j
, rule
) {
87 for (k
= 0; k
< num_attr
; k
++) {
90 if (info
[k
].found
!= NULL
) /* already found assignment */
93 if (!info
[k
].name
.name
) {
94 info
[k
].name
.name
= names
[k
];
95 info
[k
].name
.name_hash
= git_attr_file__name_hash(names
[k
]);
98 pos
= git_vector_bsearch(&rule
->assigns
, &info
[k
].name
);
99 git_clearerror(); /* okay if search failed */
102 info
[k
].found
= (git_attr_assignment
*)
103 git_vector_get(&rule
->assigns
, pos
);
104 values
[k
] = info
[k
].found
->value
;
106 if (++num_found
== num_attr
)
114 git_vector_free(&files
);
121 int git_attr_foreach(
122 git_repository
*repo
, const char *pathname
,
123 int (*callback
)(const char *name
, const char *value
, void *payload
),
128 git_vector files
= GIT_VECTOR_INIT
;
129 unsigned int i
, j
, k
;
132 git_attr_assignment
*assign
;
133 git_hashtable
*seen
= NULL
;
135 if ((error
= git_attr_path__init(
136 &path
, pathname
, git_repository_workdir(repo
))) < GIT_SUCCESS
||
137 (error
= collect_attr_files(repo
, pathname
, &files
)) < GIT_SUCCESS
)
138 return git__rethrow(error
, "Could not get attributes for %s", pathname
);
140 seen
= git_hashtable_alloc(8, git_hash__strhash_cb
, git_hash__strcmp_cb
);
146 git_vector_foreach(&files
, i
, file
) {
148 git_attr_file__foreach_matching_rule(file
, &path
, j
, rule
) {
150 git_vector_foreach(&rule
->assigns
, k
, assign
) {
151 /* skip if higher priority assignment was already seen */
152 if (git_hashtable_lookup(seen
, assign
->name
))
155 error
= git_hashtable_insert(seen
, assign
->name
, assign
);
156 if (error
!= GIT_SUCCESS
)
159 error
= callback(assign
->name
, assign
->value
, payload
);
160 if (error
!= GIT_SUCCESS
)
168 git_hashtable_free(seen
);
169 git_vector_free(&files
);
171 if (error
!= GIT_SUCCESS
)
172 (void)git__rethrow(error
, "Could not get attributes for %s", pathname
);
178 int git_attr_add_macro(
179 git_repository
*repo
,
184 git_attr_rule
*macro
= NULL
;
186 if ((error
= git_attr_cache__init(repo
)) < GIT_SUCCESS
)
189 macro
= git__calloc(1, sizeof(git_attr_rule
));
193 macro
->match
.pattern
= git__strdup(name
);
194 if (!macro
->match
.pattern
) {
199 macro
->match
.length
= strlen(macro
->match
.pattern
);
200 macro
->match
.flags
= GIT_ATTR_FNMATCH_MACRO
;
202 error
= git_attr_assignment__parse(repo
, ¯o
->assigns
, &values
);
204 if (error
== GIT_SUCCESS
)
205 error
= git_attr_cache__insert_macro(repo
, macro
);
207 if (error
< GIT_SUCCESS
)
208 git_attr_rule__free(macro
);
213 int git_attr_cache__is_cached(git_repository
*repo
, const char *path
)
215 const char *cache_key
= path
;
216 if (repo
&& git__prefixcmp(cache_key
, git_repository_workdir(repo
)) == 0)
217 cache_key
+= strlen(git_repository_workdir(repo
));
218 return (git_hashtable_lookup(repo
->attrcache
.files
, cache_key
) == NULL
);
221 int git_attr_cache__lookup_or_create_file(
222 git_repository
*repo
,
224 const char *filename
,
225 int (*loader
)(git_repository
*, const char *, git_attr_file
*),
226 git_attr_file
**file_ptr
)
229 git_attr_cache
*cache
= &repo
->attrcache
;
230 git_attr_file
*file
= NULL
;
232 file
= git_hashtable_lookup(cache
->files
, key
);
238 if (loader
&& git_path_exists(filename
) != GIT_SUCCESS
) {
243 if ((error
= git_attr_file__new(&file
)) < GIT_SUCCESS
)
247 error
= loader(repo
, filename
, file
);
249 error
= git_attr_file__set_path(repo
, key
, file
);
251 if (error
== GIT_SUCCESS
)
252 error
= git_hashtable_insert(cache
->files
, file
->path
, file
);
254 if (error
< GIT_SUCCESS
) {
255 git_attr_file__free(file
);
263 /* add git_attr_file to vector of files, loading if needed */
264 int git_attr_cache__push_file(
265 git_repository
*repo
,
268 const char *filename
,
269 int (*loader
)(git_repository
*, const char *, git_attr_file
*))
272 git_buf path
= GIT_BUF_INIT
;
273 git_attr_file
*file
= NULL
;
274 const char *cache_key
;
277 if ((error
= git_buf_joinpath(&path
, base
, filename
)) < GIT_SUCCESS
)
282 /* either get attr_file from cache or read from disk */
283 cache_key
= filename
;
284 if (repo
&& git__prefixcmp(cache_key
, git_repository_workdir(repo
)) == 0)
285 cache_key
+= strlen(git_repository_workdir(repo
));
287 error
= git_attr_cache__lookup_or_create_file(
288 repo
, cache_key
, filename
, loader
, &file
);
290 if (error
== GIT_SUCCESS
&& file
!= NULL
)
291 error
= git_vector_insert(stack
, file
);
297 #define push_attrs(R,S,B,F) \
298 git_attr_cache__push_file((R),(S),(B),(F),git_attr_file__from_file)
301 git_repository
*repo
;
305 static int push_one_attr(void *ref
, git_buf
*path
)
307 attr_walk_up_info
*info
= (attr_walk_up_info
*)ref
;
308 return push_attrs(info
->repo
, info
->files
, path
->ptr
, GIT_ATTR_FILE
);
311 static int collect_attr_files(
312 git_repository
*repo
, const char *path
, git_vector
*files
)
314 int error
= GIT_SUCCESS
;
315 git_buf dir
= GIT_BUF_INIT
;
317 const char *workdir
= git_repository_workdir(repo
);
318 attr_walk_up_info info
;
320 if ((error
= git_attr_cache__init(repo
)) < GIT_SUCCESS
)
323 if ((error
= git_vector_init(files
, 4, NULL
)) < GIT_SUCCESS
)
326 if ((error
= git_path_find_dir(&dir
, path
, workdir
)) < GIT_SUCCESS
)
329 /* in precendence order highest to lowest:
330 * - $GIT_DIR/info/attributes
331 * - path components with .gitattributes
332 * - config core.attributesfile
333 * - $GIT_PREFIX/etc/gitattributes
336 error
= push_attrs(repo
, files
, repo
->path_repository
, GIT_ATTR_FILE_INREPO
);
337 if (error
< GIT_SUCCESS
)
342 error
= git_path_walk_up(&dir
, workdir
, push_one_attr
, &info
);
343 if (error
< GIT_SUCCESS
)
346 if ((error
= git_repository_config(&cfg
, repo
)) == GIT_SUCCESS
) {
347 const char *core_attribs
= NULL
;
348 git_config_get_string(cfg
, GIT_ATTR_CONFIG
, &core_attribs
);
349 git_clearerror(); /* don't care if attributesfile is not set */
351 error
= push_attrs(repo
, files
, NULL
, core_attribs
);
352 git_config_free(cfg
);
355 if (error
== GIT_SUCCESS
) {
356 error
= git_futils_find_system_file(&dir
, GIT_ATTR_FILE_SYSTEM
);
357 if (error
== GIT_SUCCESS
)
358 error
= push_attrs(repo
, files
, NULL
, dir
.ptr
);
359 else if (error
== GIT_ENOTFOUND
)
364 if (error
< GIT_SUCCESS
) {
365 git__rethrow(error
, "Could not get attributes for '%s'", path
);
366 git_vector_free(files
);
374 int git_attr_cache__init(git_repository
*repo
)
376 int error
= GIT_SUCCESS
;
377 git_attr_cache
*cache
= &repo
->attrcache
;
379 if (cache
->initialized
)
382 if (cache
->files
== NULL
) {
383 cache
->files
= git_hashtable_alloc(
384 8, git_hash__strhash_cb
, git_hash__strcmp_cb
);
386 return git__throw(GIT_ENOMEM
, "Could not initialize attribute cache");
389 if (cache
->macros
== NULL
) {
390 cache
->macros
= git_hashtable_alloc(
391 8, git_hash__strhash_cb
, git_hash__strcmp_cb
);
393 return git__throw(GIT_ENOMEM
, "Could not initialize attribute cache");
396 cache
->initialized
= 1;
398 /* insert default macros */
399 error
= git_attr_add_macro(repo
, "binary", "-diff -crlf");
404 void git_attr_cache_flush(
405 git_repository
*repo
)
410 if (repo
->attrcache
.files
) {
411 const void *GIT_UNUSED(name
);
414 GIT_HASHTABLE_FOREACH(repo
->attrcache
.files
, name
, file
,
415 git_attr_file__free(file
));
417 git_hashtable_free(repo
->attrcache
.files
);
418 repo
->attrcache
.files
= NULL
;
421 if (repo
->attrcache
.macros
) {
422 const void *GIT_UNUSED(name
);
425 GIT_HASHTABLE_FOREACH(repo
->attrcache
.macros
, name
, rule
,
426 git_attr_rule__free(rule
));
428 git_hashtable_free(repo
->attrcache
.macros
);
429 repo
->attrcache
.macros
= NULL
;
432 repo
->attrcache
.initialized
= 0;
435 int git_attr_cache__insert_macro(git_repository
*repo
, git_attr_rule
*macro
)
437 if (macro
->assigns
.length
== 0)
438 return git__throw(GIT_EMISSINGOBJDATA
, "git attribute macro with no values");
440 return git_hashtable_insert(
441 repo
->attrcache
.macros
, macro
->match
.pattern
, macro
);