]>
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
))) < 0 ||
26 (error
= collect_attr_files(repo
, pathname
, &files
)) < 0)
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
);
37 *value
= ((git_attr_assignment
*)git_vector_get(
38 &rule
->assigns
, pos
))->value
;
45 git_vector_free(&files
);
53 git_attr_assignment
*found
;
56 int git_attr_get_many(
57 git_repository
*repo
, const char *pathname
,
58 size_t num_attr
, const char **names
, const char **values
)
62 git_vector files
= GIT_VECTOR_INIT
;
66 attr_get_many_info
*info
= NULL
;
69 memset((void *)values
, 0, sizeof(const char *) * num_attr
);
71 if ((error
= git_attr_path__init(
72 &path
, pathname
, git_repository_workdir(repo
))) < 0 ||
73 (error
= collect_attr_files(repo
, pathname
, &files
)) < 0)
76 info
= git__calloc(num_attr
, sizeof(attr_get_many_info
));
77 GITERR_CHECK_ALLOC(info
);
79 git_vector_foreach(&files
, i
, file
) {
81 git_attr_file__foreach_matching_rule(file
, &path
, j
, rule
) {
83 for (k
= 0; k
< num_attr
; k
++) {
86 if (info
[k
].found
!= NULL
) /* already found assignment */
89 if (!info
[k
].name
.name
) {
90 info
[k
].name
.name
= names
[k
];
91 info
[k
].name
.name_hash
= git_attr_file__name_hash(names
[k
]);
94 pos
= git_vector_bsearch(&rule
->assigns
, &info
[k
].name
);
96 info
[k
].found
= (git_attr_assignment
*)
97 git_vector_get(&rule
->assigns
, pos
);
98 values
[k
] = info
[k
].found
->value
;
100 if (++num_found
== num_attr
)
108 git_vector_free(&files
);
115 int git_attr_foreach(
116 git_repository
*repo
, const char *pathname
,
117 int (*callback
)(const char *name
, const char *value
, void *payload
),
122 git_vector files
= GIT_VECTOR_INIT
;
123 unsigned int i
, j
, k
;
126 git_attr_assignment
*assign
;
127 git_hashtable
*seen
= NULL
;
129 if ((error
= git_attr_path__init(
130 &path
, pathname
, git_repository_workdir(repo
))) < 0 ||
131 (error
= collect_attr_files(repo
, pathname
, &files
)) < 0)
134 seen
= git_hashtable_alloc(8, git_hash__strhash_cb
, git_hash__strcmp_cb
);
135 GITERR_CHECK_ALLOC(seen
);
137 git_vector_foreach(&files
, i
, file
) {
139 git_attr_file__foreach_matching_rule(file
, &path
, j
, rule
) {
141 git_vector_foreach(&rule
->assigns
, k
, assign
) {
142 /* skip if higher priority assignment was already seen */
143 if (git_hashtable_lookup(seen
, assign
->name
))
146 if (!(error
= git_hashtable_insert(seen
, assign
->name
, assign
)))
147 error
= callback(assign
->name
, assign
->value
, payload
);
156 git_hashtable_free(seen
);
157 git_vector_free(&files
);
163 int git_attr_add_macro(
164 git_repository
*repo
,
169 git_attr_rule
*macro
= NULL
;
171 if (git_attr_cache__init(repo
) < 0)
174 macro
= git__calloc(1, sizeof(git_attr_rule
));
175 GITERR_CHECK_ALLOC(macro
);
177 macro
->match
.pattern
= git__strdup(name
);
178 GITERR_CHECK_ALLOC(macro
->match
.pattern
);
180 macro
->match
.length
= strlen(macro
->match
.pattern
);
181 macro
->match
.flags
= GIT_ATTR_FNMATCH_MACRO
;
183 error
= git_attr_assignment__parse(repo
, ¯o
->assigns
, &values
);
186 error
= git_attr_cache__insert_macro(repo
, macro
);
189 git_attr_rule__free(macro
);
194 bool git_attr_cache__is_cached(git_repository
*repo
, const char *path
)
196 const char *cache_key
= path
;
197 if (repo
&& git__prefixcmp(cache_key
, git_repository_workdir(repo
)) == 0)
198 cache_key
+= strlen(git_repository_workdir(repo
));
199 return (git_hashtable_lookup(
200 git_repository_attr_cache(repo
)->files
, cache_key
) != NULL
);
203 int git_attr_cache__lookup_or_create_file(
204 git_repository
*repo
,
206 const char *filename
,
207 int (*loader
)(git_repository
*, const char *, git_attr_file
*),
208 git_attr_file
**file_ptr
)
211 git_attr_cache
*cache
= git_repository_attr_cache(repo
);
212 git_attr_file
*file
= NULL
;
214 if ((file
= git_hashtable_lookup(cache
->files
, key
)) != NULL
) {
219 if (loader
&& git_path_exists(filename
) == false) {
224 if (git_attr_file__new(&file
) < 0)
228 error
= loader(repo
, filename
, file
);
230 error
= git_attr_file__set_path(repo
, key
, file
);
233 error
= git_hashtable_insert(cache
->files
, file
->path
, file
);
236 git_attr_file__free(file
);
244 /* add git_attr_file to vector of files, loading if needed */
245 int git_attr_cache__push_file(
246 git_repository
*repo
,
249 const char *filename
,
250 int (*loader
)(git_repository
*, const char *, git_attr_file
*))
253 git_buf path
= GIT_BUF_INIT
;
254 git_attr_file
*file
= NULL
;
255 const char *cache_key
;
258 if (git_buf_joinpath(&path
, base
, filename
) < 0)
263 /* either get attr_file from cache or read from disk */
264 cache_key
= filename
;
265 if (repo
&& git__prefixcmp(cache_key
, git_repository_workdir(repo
)) == 0)
266 cache_key
+= strlen(git_repository_workdir(repo
));
268 error
= git_attr_cache__lookup_or_create_file(
269 repo
, cache_key
, filename
, loader
, &file
);
271 if (!error
&& file
!= NULL
)
272 error
= git_vector_insert(stack
, file
);
278 #define push_attrs(R,S,B,F) \
279 git_attr_cache__push_file((R),(S),(B),(F),git_attr_file__from_file)
282 git_repository
*repo
;
286 static int push_one_attr(void *ref
, git_buf
*path
)
288 attr_walk_up_info
*info
= (attr_walk_up_info
*)ref
;
289 return push_attrs(info
->repo
, info
->files
, path
->ptr
, GIT_ATTR_FILE
);
292 static int collect_attr_files(
293 git_repository
*repo
, const char *path
, git_vector
*files
)
296 git_buf dir
= GIT_BUF_INIT
;
297 const char *workdir
= git_repository_workdir(repo
);
298 attr_walk_up_info info
;
300 if (git_attr_cache__init(repo
) < 0 ||
301 git_vector_init(files
, 4, NULL
) < 0)
304 error
= git_path_find_dir(&dir
, path
, workdir
);
308 /* in precendence order highest to lowest:
309 * - $GIT_DIR/info/attributes
310 * - path components with .gitattributes
311 * - config core.attributesfile
312 * - $GIT_PREFIX/etc/gitattributes
316 repo
, files
, git_repository_path(repo
), GIT_ATTR_FILE_INREPO
);
322 error
= git_path_walk_up(&dir
, workdir
, push_one_attr
, &info
);
326 if (git_repository_attr_cache(repo
)->cfg_attr_file
!= NULL
) {
328 repo
, files
, NULL
, git_repository_attr_cache(repo
)->cfg_attr_file
);
333 error
= git_futils_find_system_file(&dir
, GIT_ATTR_FILE_SYSTEM
);
335 error
= push_attrs(repo
, files
, NULL
, dir
.ptr
);
336 else if (error
== GIT_ENOTFOUND
)
341 git_vector_free(files
);
348 int git_attr_cache__init(git_repository
*repo
)
351 git_attr_cache
*cache
= git_repository_attr_cache(repo
);
354 if (cache
->initialized
)
357 /* cache config settings for attributes and ignores */
358 if (git_repository_config__weakptr(&cfg
, repo
) < 0)
361 ret
= git_config_get_string(cfg
, GIT_ATTR_CONFIG
, &cache
->cfg_attr_file
);
362 if (ret
< 0 && ret
!= GIT_ENOTFOUND
)
365 ret
= git_config_get_string(cfg
, GIT_IGNORE_CONFIG
, &cache
->cfg_excl_file
);
366 if (ret
< 0 && ret
!= GIT_ENOTFOUND
)
371 /* allocate hashtable for attribute and ignore file contents */
372 if (cache
->files
== NULL
) {
373 cache
->files
= git_hashtable_alloc(
374 8, git_hash__strhash_cb
, git_hash__strcmp_cb
);
379 /* allocate hashtable for attribute macros */
380 if (cache
->macros
== NULL
) {
381 cache
->macros
= git_hashtable_alloc(
382 8, git_hash__strhash_cb
, git_hash__strcmp_cb
);
387 cache
->initialized
= 1;
389 /* insert default macros */
390 return git_attr_add_macro(repo
, "binary", "-diff -crlf -text");
393 void git_attr_cache_flush(
394 git_repository
*repo
)
396 git_hashtable
*table
;
401 if ((table
= git_repository_attr_cache(repo
)->files
) != NULL
) {
404 GIT_HASHTABLE_FOREACH_VALUE(table
, file
, git_attr_file__free(file
));
405 git_hashtable_free(table
);
407 git_repository_attr_cache(repo
)->files
= NULL
;
410 if ((table
= git_repository_attr_cache(repo
)->macros
) != NULL
) {
413 GIT_HASHTABLE_FOREACH_VALUE(table
, rule
, git_attr_rule__free(rule
));
414 git_hashtable_free(table
);
416 git_repository_attr_cache(repo
)->macros
= NULL
;
419 git_repository_attr_cache(repo
)->initialized
= 0;
422 int git_attr_cache__insert_macro(git_repository
*repo
, git_attr_rule
*macro
)
424 /* TODO: generate warning log if (macro->assigns.length == 0) */
425 if (macro
->assigns
.length
== 0)
428 return git_hashtable_insert(
429 git_repository_attr_cache(repo
)->macros
, macro
->match
.pattern
, macro
);