]>
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(&path
, pathname
)) < GIT_SUCCESS
||
25 (error
= collect_attr_files(repo
, pathname
, &files
)) < GIT_SUCCESS
)
26 return git__rethrow(error
, "Could not get attribute for %s", pathname
);
29 attr
.name_hash
= git_attr_file__name_hash(name
);
31 git_vector_foreach(&files
, i
, file
) {
33 git_attr_file__foreach_matching_rule(file
, &path
, j
, rule
) {
34 int pos
= git_vector_bsearch(&rule
->assigns
, &attr
);
35 git_clearerror(); /* okay if search failed */
38 *value
= ((git_attr_assignment
*)git_vector_get(
39 &rule
->assigns
, pos
))->value
;
46 git_vector_free(&files
);
54 git_attr_assignment
*found
;
57 int git_attr_get_many(
58 git_repository
*repo
, const char *pathname
,
59 size_t num_attr
, const char **names
, const char **values
)
63 git_vector files
= GIT_VECTOR_INIT
;
67 attr_get_many_info
*info
= NULL
;
70 memset((void *)values
, 0, sizeof(const char *) * num_attr
);
72 if ((error
= git_attr_path__init(&path
, pathname
)) < GIT_SUCCESS
||
73 (error
= collect_attr_files(repo
, pathname
, &files
)) < GIT_SUCCESS
)
74 return git__rethrow(error
, "Could not get attributes for %s", pathname
);
76 if ((info
= git__calloc(num_attr
, sizeof(attr_get_many_info
))) == NULL
) {
77 git__rethrow(GIT_ENOMEM
, "Could not get attributes for %s", pathname
);
81 git_vector_foreach(&files
, i
, file
) {
83 git_attr_file__foreach_matching_rule(file
, &path
, j
, rule
) {
85 for (k
= 0; k
< num_attr
; k
++) {
88 if (info
[k
].found
!= NULL
) /* already found assignment */
91 if (!info
[k
].name
.name
) {
92 info
[k
].name
.name
= names
[k
];
93 info
[k
].name
.name_hash
= git_attr_file__name_hash(names
[k
]);
96 pos
= git_vector_bsearch(&rule
->assigns
, &info
[k
].name
);
97 git_clearerror(); /* okay if search failed */
100 info
[k
].found
= (git_attr_assignment
*)
101 git_vector_get(&rule
->assigns
, pos
);
102 values
[k
] = info
[k
].found
->value
;
104 if (++num_found
== num_attr
)
112 git_vector_free(&files
);
119 int git_attr_foreach(
120 git_repository
*repo
, const char *pathname
,
121 int (*callback
)(const char *name
, const char *value
, void *payload
),
126 git_vector files
= GIT_VECTOR_INIT
;
127 unsigned int i
, j
, k
;
130 git_attr_assignment
*assign
;
131 git_hashtable
*seen
= NULL
;
133 if ((error
= git_attr_path__init(&path
, pathname
)) < GIT_SUCCESS
||
134 (error
= collect_attr_files(repo
, pathname
, &files
)) < GIT_SUCCESS
)
135 return git__rethrow(error
, "Could not get attributes for %s", pathname
);
137 seen
= git_hashtable_alloc(8, git_hash__strhash_cb
, git_hash__strcmp_cb
);
143 git_vector_foreach(&files
, i
, file
) {
145 git_attr_file__foreach_matching_rule(file
, &path
, j
, rule
) {
147 git_vector_foreach(&rule
->assigns
, k
, assign
) {
148 /* skip if higher priority assignment was already seen */
149 if (git_hashtable_lookup(seen
, assign
->name
))
152 error
= git_hashtable_insert(seen
, assign
->name
, assign
);
153 if (error
!= GIT_SUCCESS
)
156 error
= callback(assign
->name
, assign
->value
, payload
);
157 if (error
!= GIT_SUCCESS
)
165 git_hashtable_free(seen
);
166 git_vector_free(&files
);
168 if (error
!= GIT_SUCCESS
)
169 (void)git__rethrow(error
, "Could not get attributes for %s", pathname
);
175 int git_attr_add_macro(
176 git_repository
*repo
,
181 git_attr_rule
*macro
= NULL
;
183 if ((error
= git_attr_cache__init(repo
)) < GIT_SUCCESS
)
186 macro
= git__calloc(1, sizeof(git_attr_rule
));
190 macro
->match
.pattern
= git__strdup(name
);
191 if (!macro
->match
.pattern
) {
196 macro
->match
.length
= strlen(macro
->match
.pattern
);
197 macro
->match
.flags
= GIT_ATTR_FNMATCH_MACRO
;
199 error
= git_attr_assignment__parse(repo
, ¯o
->assigns
, &values
);
201 if (error
== GIT_SUCCESS
)
202 error
= git_attr_cache__insert_macro(repo
, macro
);
204 if (error
< GIT_SUCCESS
)
205 git_attr_rule__free(macro
);
211 /* add git_attr_file to vector of files, loading if needed */
212 int git_attr_cache__push_file(
213 git_repository
*repo
,
216 const char *filename
,
217 int (*loader
)(git_repository
*, const char *, git_attr_file
**))
219 int error
= GIT_SUCCESS
;
220 git_attr_cache
*cache
= &repo
->attrcache
;
221 git_buf path
= GIT_BUF_INIT
;
223 int add_to_cache
= 0;
226 if ((error
= git_buf_joinpath(&path
, base
, filename
)) < GIT_SUCCESS
)
231 /* either get attr_file from cache or read from disk */
232 file
= git_hashtable_lookup(cache
->files
, filename
);
233 if (file
== NULL
&& git_futils_exists(filename
) == GIT_SUCCESS
) {
234 error
= (*loader
)(repo
, filename
, &file
);
235 add_to_cache
= (error
== GIT_SUCCESS
);
239 /* add file to vector, if we found it */
240 error
= git_vector_insert(stack
, file
);
242 /* add file to cache, if it is new */
243 /* do this after above step b/c it is not critical */
244 if (error
== GIT_SUCCESS
&& add_to_cache
&& file
->path
!= NULL
)
245 error
= git_hashtable_insert(cache
->files
, file
->path
, file
);
253 #define push_attrs(R,S,B,F) \
254 git_attr_cache__push_file((R),(S),(B),(F),git_attr_file__from_file)
257 git_repository
*repo
;
261 static int push_one_attr(void *ref
, git_buf
*path
)
263 attr_walk_up_info
*info
= (attr_walk_up_info
*)ref
;
264 return push_attrs(info
->repo
, info
->files
, path
->ptr
, GIT_ATTR_FILE
);
267 static int collect_attr_files(
268 git_repository
*repo
, const char *path
, git_vector
*files
)
270 int error
= GIT_SUCCESS
;
271 git_buf dir
= GIT_BUF_INIT
;
273 const char *workdir
= git_repository_workdir(repo
);
274 attr_walk_up_info info
;
276 if ((error
= git_attr_cache__init(repo
)) < GIT_SUCCESS
)
279 if ((error
= git_vector_init(files
, 4, NULL
)) < GIT_SUCCESS
)
282 if ((error
= git_futils_dir_for_path(&dir
, path
, workdir
)) < GIT_SUCCESS
)
285 /* in precendence order highest to lowest:
286 * - $GIT_DIR/info/attributes
287 * - path components with .gitattributes
288 * - config core.attributesfile
289 * - $GIT_PREFIX/etc/gitattributes
292 error
= push_attrs(repo
, files
, repo
->path_repository
, GIT_ATTR_FILE_INREPO
);
293 if (error
< GIT_SUCCESS
)
298 error
= git_path_walk_up(&dir
, workdir
, push_one_attr
, &info
);
299 if (error
< GIT_SUCCESS
)
302 if ((error
= git_repository_config(&cfg
, repo
)) == GIT_SUCCESS
) {
303 const char *core_attribs
= NULL
;
304 git_config_get_string(cfg
, GIT_ATTR_CONFIG
, &core_attribs
);
305 git_clearerror(); /* don't care if attributesfile is not set */
307 error
= push_attrs(repo
, files
, NULL
, core_attribs
);
308 git_config_free(cfg
);
311 if (error
== GIT_SUCCESS
) {
312 error
= git_futils_find_system_file(&dir
, GIT_ATTR_FILE_SYSTEM
);
313 if (error
== GIT_SUCCESS
)
314 error
= push_attrs(repo
, files
, NULL
, dir
.ptr
);
315 else if (error
== GIT_ENOTFOUND
)
320 if (error
< GIT_SUCCESS
) {
321 git__rethrow(error
, "Could not get attributes for '%s'", path
);
322 git_vector_free(files
);
330 int git_attr_cache__init(git_repository
*repo
)
332 int error
= GIT_SUCCESS
;
333 git_attr_cache
*cache
= &repo
->attrcache
;
335 if (cache
->initialized
)
338 if (cache
->files
== NULL
) {
339 cache
->files
= git_hashtable_alloc(
340 8, git_hash__strhash_cb
, git_hash__strcmp_cb
);
342 return git__throw(GIT_ENOMEM
, "Could not initialize attribute cache");
345 if (cache
->macros
== NULL
) {
346 cache
->macros
= git_hashtable_alloc(
347 8, git_hash__strhash_cb
, git_hash__strcmp_cb
);
349 return git__throw(GIT_ENOMEM
, "Could not initialize attribute cache");
352 cache
->initialized
= 1;
354 /* insert default macros */
355 error
= git_attr_add_macro(repo
, "binary", "-diff -crlf");
360 void git_attr_cache_flush(
361 git_repository
*repo
)
366 if (repo
->attrcache
.files
) {
367 const void *GIT_UNUSED(name
);
370 GIT_HASHTABLE_FOREACH(repo
->attrcache
.files
, name
, file
,
371 git_attr_file__free(file
));
373 git_hashtable_free(repo
->attrcache
.files
);
374 repo
->attrcache
.files
= NULL
;
377 if (repo
->attrcache
.macros
) {
378 const void *GIT_UNUSED(name
);
381 GIT_HASHTABLE_FOREACH(repo
->attrcache
.macros
, name
, rule
,
382 git_attr_rule__free(rule
));
384 git_hashtable_free(repo
->attrcache
.macros
);
385 repo
->attrcache
.macros
= NULL
;
388 repo
->attrcache
.initialized
= 0;
391 int git_attr_cache__insert_macro(git_repository
*repo
, git_attr_rule
*macro
)
393 if (macro
->assigns
.length
== 0)
394 return git__throw(GIT_EMISSINGOBJDATA
, "git attribute macro with no values");
396 return git_hashtable_insert(
397 repo
->attrcache
.macros
, macro
->match
.pattern
, macro
);