]>
Commit | Line | Data |
---|---|---|
73b51450 | 1 | #include "repository.h" |
ee1f0b1a RB |
2 | #include "fileops.h" |
3 | #include "config.h" | |
4 | #include <ctype.h> | |
5 | ||
01fed0a8 RB |
6 | GIT_KHASH_STR__IMPLEMENTATION; |
7 | ||
ee1f0b1a RB |
8 | static int collect_attr_files( |
9 | git_repository *repo, const char *path, git_vector *files); | |
10 | ||
11 | ||
12 | int git_attr_get( | |
13 | git_repository *repo, const char *pathname, | |
14 | const char *name, const char **value) | |
15 | { | |
16 | int error; | |
17 | git_attr_path path; | |
18 | git_vector files = GIT_VECTOR_INIT; | |
19 | unsigned int i, j; | |
20 | git_attr_file *file; | |
21 | git_attr_name attr; | |
22 | git_attr_rule *rule; | |
23 | ||
24 | *value = NULL; | |
25 | ||
adc9bdb3 | 26 | if ((error = git_attr_path__init( |
0d0fa7c3 RB |
27 | &path, pathname, git_repository_workdir(repo))) < 0 || |
28 | (error = collect_attr_files(repo, pathname, &files)) < 0) | |
29 | return error; | |
ee1f0b1a RB |
30 | |
31 | attr.name = name; | |
32 | attr.name_hash = git_attr_file__name_hash(name); | |
33 | ||
34 | git_vector_foreach(&files, i, file) { | |
35 | ||
36 | git_attr_file__foreach_matching_rule(file, &path, j, rule) { | |
37 | int pos = git_vector_bsearch(&rule->assigns, &attr); | |
ee1f0b1a RB |
38 | if (pos >= 0) { |
39 | *value = ((git_attr_assignment *)git_vector_get( | |
40 | &rule->assigns, pos))->value; | |
41 | goto found; | |
42 | } | |
43 | } | |
44 | } | |
45 | ||
46 | found: | |
47 | git_vector_free(&files); | |
48 | ||
49 | return error; | |
50 | } | |
51 | ||
52 | ||
53 | typedef struct { | |
54 | git_attr_name name; | |
55 | git_attr_assignment *found; | |
56 | } attr_get_many_info; | |
57 | ||
58 | int git_attr_get_many( | |
59 | git_repository *repo, const char *pathname, | |
60 | size_t num_attr, const char **names, const char **values) | |
61 | { | |
62 | int error; | |
63 | git_attr_path path; | |
64 | git_vector files = GIT_VECTOR_INIT; | |
65 | unsigned int i, j, k; | |
66 | git_attr_file *file; | |
67 | git_attr_rule *rule; | |
68 | attr_get_many_info *info = NULL; | |
69 | size_t num_found = 0; | |
70 | ||
acb159e1 | 71 | memset((void *)values, 0, sizeof(const char *) * num_attr); |
ee1f0b1a | 72 | |
adc9bdb3 | 73 | if ((error = git_attr_path__init( |
0d0fa7c3 RB |
74 | &path, pathname, git_repository_workdir(repo))) < 0 || |
75 | (error = collect_attr_files(repo, pathname, &files)) < 0) | |
76 | return error; | |
ee1f0b1a | 77 | |
0d0fa7c3 RB |
78 | info = git__calloc(num_attr, sizeof(attr_get_many_info)); |
79 | GITERR_CHECK_ALLOC(info); | |
ee1f0b1a RB |
80 | |
81 | git_vector_foreach(&files, i, file) { | |
82 | ||
83 | git_attr_file__foreach_matching_rule(file, &path, j, rule) { | |
84 | ||
85 | for (k = 0; k < num_attr; k++) { | |
86 | int pos; | |
87 | ||
88 | if (info[k].found != NULL) /* already found assignment */ | |
89 | continue; | |
90 | ||
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]); | |
94 | } | |
95 | ||
96 | pos = git_vector_bsearch(&rule->assigns, &info[k].name); | |
ee1f0b1a RB |
97 | if (pos >= 0) { |
98 | info[k].found = (git_attr_assignment *) | |
99 | git_vector_get(&rule->assigns, pos); | |
100 | values[k] = info[k].found->value; | |
101 | ||
102 | if (++num_found == num_attr) | |
103 | goto cleanup; | |
104 | } | |
105 | } | |
106 | } | |
107 | } | |
108 | ||
109 | cleanup: | |
110 | git_vector_free(&files); | |
111 | git__free(info); | |
112 | ||
113 | return error; | |
114 | } | |
115 | ||
116 | ||
117 | int git_attr_foreach( | |
118 | git_repository *repo, const char *pathname, | |
119 | int (*callback)(const char *name, const char *value, void *payload), | |
120 | void *payload) | |
121 | { | |
122 | int error; | |
123 | git_attr_path path; | |
124 | git_vector files = GIT_VECTOR_INIT; | |
125 | unsigned int i, j, k; | |
126 | git_attr_file *file; | |
127 | git_attr_rule *rule; | |
128 | git_attr_assignment *assign; | |
01fed0a8 | 129 | git_khash_str *seen = NULL; |
ee1f0b1a | 130 | |
adc9bdb3 | 131 | if ((error = git_attr_path__init( |
0d0fa7c3 RB |
132 | &path, pathname, git_repository_workdir(repo))) < 0 || |
133 | (error = collect_attr_files(repo, pathname, &files)) < 0) | |
134 | return error; | |
ee1f0b1a | 135 | |
01fed0a8 | 136 | seen = git_khash_str_alloc(); |
0d0fa7c3 | 137 | GITERR_CHECK_ALLOC(seen); |
ee1f0b1a RB |
138 | |
139 | git_vector_foreach(&files, i, file) { | |
140 | ||
141 | git_attr_file__foreach_matching_rule(file, &path, j, rule) { | |
142 | ||
143 | git_vector_foreach(&rule->assigns, k, assign) { | |
144 | /* skip if higher priority assignment was already seen */ | |
01fed0a8 | 145 | if (git_khash_str_exists(seen, assign->name)) |
ee1f0b1a RB |
146 | continue; |
147 | ||
01fed0a8 RB |
148 | git_khash_str_insert(seen, assign->name, assign, error); |
149 | if (error >= 0) | |
0d0fa7c3 | 150 | error = callback(assign->name, assign->value, payload); |
ee1f0b1a | 151 | |
0d0fa7c3 | 152 | if (error != 0) |
ee1f0b1a RB |
153 | goto cleanup; |
154 | } | |
155 | } | |
156 | } | |
157 | ||
158 | cleanup: | |
01fed0a8 | 159 | git_khash_str_free(seen); |
ee1f0b1a RB |
160 | git_vector_free(&files); |
161 | ||
ee1f0b1a RB |
162 | return error; |
163 | } | |
164 | ||
165 | ||
73b51450 RB |
166 | int git_attr_add_macro( |
167 | git_repository *repo, | |
168 | const char *name, | |
169 | const char *values) | |
170 | { | |
171 | int error; | |
172 | git_attr_rule *macro = NULL; | |
19fa2bc1 | 173 | git_pool *pool; |
73b51450 | 174 | |
0d0fa7c3 RB |
175 | if (git_attr_cache__init(repo) < 0) |
176 | return -1; | |
73b51450 RB |
177 | |
178 | macro = git__calloc(1, sizeof(git_attr_rule)); | |
0d0fa7c3 | 179 | GITERR_CHECK_ALLOC(macro); |
73b51450 | 180 | |
19fa2bc1 RB |
181 | pool = &git_repository_attr_cache(repo)->pool; |
182 | ||
183 | macro->match.pattern = git_pool_strdup(pool, name); | |
0d0fa7c3 | 184 | GITERR_CHECK_ALLOC(macro->match.pattern); |
73b51450 RB |
185 | |
186 | macro->match.length = strlen(macro->match.pattern); | |
187 | macro->match.flags = GIT_ATTR_FNMATCH_MACRO; | |
188 | ||
19fa2bc1 | 189 | error = git_attr_assignment__parse(repo, pool, ¯o->assigns, &values); |
73b51450 | 190 | |
0d0fa7c3 | 191 | if (!error) |
73b51450 RB |
192 | error = git_attr_cache__insert_macro(repo, macro); |
193 | ||
0d0fa7c3 | 194 | if (error < 0) |
73b51450 | 195 | git_attr_rule__free(macro); |
73b51450 RB |
196 | |
197 | return error; | |
198 | } | |
199 | ||
0d0fa7c3 | 200 | bool git_attr_cache__is_cached(git_repository *repo, const char *path) |
e8c96ed2 RB |
201 | { |
202 | const char *cache_key = path; | |
01fed0a8 RB |
203 | git_khash_str *files = git_repository_attr_cache(repo)->files; |
204 | ||
e8c96ed2 RB |
205 | if (repo && git__prefixcmp(cache_key, git_repository_workdir(repo)) == 0) |
206 | cache_key += strlen(git_repository_workdir(repo)); | |
01fed0a8 RB |
207 | |
208 | return git_khash_str_exists(files, cache_key); | |
e8c96ed2 | 209 | } |
73b51450 | 210 | |
b6c93aef RB |
211 | int git_attr_cache__lookup_or_create_file( |
212 | git_repository *repo, | |
213 | const char *key, | |
214 | const char *filename, | |
215 | int (*loader)(git_repository *, const char *, git_attr_file *), | |
216 | git_attr_file **file_ptr) | |
217 | { | |
218 | int error; | |
95dfb031 | 219 | git_attr_cache *cache = git_repository_attr_cache(repo); |
b6c93aef | 220 | git_attr_file *file = NULL; |
01fed0a8 | 221 | khiter_t pos; |
b6c93aef | 222 | |
01fed0a8 RB |
223 | pos = git_khash_str_lookup_index(cache->files, key); |
224 | if (git_khash_str_valid_index(cache->files, pos)) { | |
225 | *file_ptr = git_khash_str_value_at(cache->files, pos); | |
0d0fa7c3 | 226 | return 0; |
b6c93aef RB |
227 | } |
228 | ||
9d160ba8 | 229 | if (loader && git_path_exists(filename) == false) { |
b6c93aef | 230 | *file_ptr = NULL; |
0d0fa7c3 | 231 | return 0; |
b6c93aef RB |
232 | } |
233 | ||
19fa2bc1 | 234 | if (git_attr_file__new(&file, &cache->pool) < 0) |
0d0fa7c3 | 235 | return -1; |
b6c93aef RB |
236 | |
237 | if (loader) | |
238 | error = loader(repo, filename, file); | |
239 | else | |
240 | error = git_attr_file__set_path(repo, key, file); | |
241 | ||
01fed0a8 RB |
242 | if (!error) { |
243 | git_khash_str_insert(cache->files, file->path, file, error); | |
244 | if (error > 0) | |
245 | error = 0; | |
246 | } | |
b6c93aef | 247 | |
0d0fa7c3 | 248 | if (error < 0) { |
b6c93aef RB |
249 | git_attr_file__free(file); |
250 | file = NULL; | |
251 | } | |
252 | ||
253 | *file_ptr = file; | |
254 | return error; | |
255 | } | |
256 | ||
ee1f0b1a | 257 | /* add git_attr_file to vector of files, loading if needed */ |
df743c7d | 258 | int git_attr_cache__push_file( |
ee1f0b1a | 259 | git_repository *repo, |
df743c7d | 260 | git_vector *stack, |
ee1f0b1a | 261 | const char *base, |
df743c7d | 262 | const char *filename, |
a51cd8e6 | 263 | int (*loader)(git_repository *, const char *, git_attr_file *)) |
ee1f0b1a | 264 | { |
b6c93aef | 265 | int error; |
ee1f0b1a | 266 | git_buf path = GIT_BUF_INIT; |
62a1f713 | 267 | git_attr_file *file = NULL; |
62a1f713 | 268 | const char *cache_key; |
ee1f0b1a | 269 | |
1dbcc9fc | 270 | if (base != NULL) { |
0d0fa7c3 RB |
271 | if (git_buf_joinpath(&path, base, filename) < 0) |
272 | return -1; | |
1dbcc9fc RB |
273 | filename = path.ptr; |
274 | } | |
ee1f0b1a RB |
275 | |
276 | /* either get attr_file from cache or read from disk */ | |
62a1f713 RB |
277 | cache_key = filename; |
278 | if (repo && git__prefixcmp(cache_key, git_repository_workdir(repo)) == 0) | |
279 | cache_key += strlen(git_repository_workdir(repo)); | |
280 | ||
b6c93aef RB |
281 | error = git_attr_cache__lookup_or_create_file( |
282 | repo, cache_key, filename, loader, &file); | |
ee1f0b1a | 283 | |
0d0fa7c3 | 284 | if (!error && file != NULL) |
df743c7d | 285 | error = git_vector_insert(stack, file); |
ee1f0b1a | 286 | |
ee1f0b1a RB |
287 | git_buf_free(&path); |
288 | return error; | |
289 | } | |
290 | ||
df743c7d RB |
291 | #define push_attrs(R,S,B,F) \ |
292 | git_attr_cache__push_file((R),(S),(B),(F),git_attr_file__from_file) | |
ee1f0b1a | 293 | |
0cfcff5d RB |
294 | typedef struct { |
295 | git_repository *repo; | |
296 | git_vector *files; | |
297 | } attr_walk_up_info; | |
298 | ||
299 | static int push_one_attr(void *ref, git_buf *path) | |
300 | { | |
301 | attr_walk_up_info *info = (attr_walk_up_info *)ref; | |
302 | return push_attrs(info->repo, info->files, path->ptr, GIT_ATTR_FILE); | |
303 | } | |
304 | ||
ee1f0b1a RB |
305 | static int collect_attr_files( |
306 | git_repository *repo, const char *path, git_vector *files) | |
307 | { | |
0d0fa7c3 | 308 | int error; |
ee1f0b1a | 309 | git_buf dir = GIT_BUF_INIT; |
ee1f0b1a | 310 | const char *workdir = git_repository_workdir(repo); |
0cfcff5d | 311 | attr_walk_up_info info; |
ee1f0b1a | 312 | |
ab43ad2f RB |
313 | if (git_attr_cache__init(repo) < 0 || |
314 | git_vector_init(files, 4, NULL) < 0) | |
315 | return -1; | |
ee1f0b1a | 316 | |
ab43ad2f RB |
317 | error = git_path_find_dir(&dir, path, workdir); |
318 | if (error < 0) | |
ee1f0b1a RB |
319 | goto cleanup; |
320 | ||
ee1f0b1a RB |
321 | /* in precendence order highest to lowest: |
322 | * - $GIT_DIR/info/attributes | |
323 | * - path components with .gitattributes | |
324 | * - config core.attributesfile | |
325 | * - $GIT_PREFIX/etc/gitattributes | |
326 | */ | |
327 | ||
95dfb031 RB |
328 | error = push_attrs( |
329 | repo, files, git_repository_path(repo), GIT_ATTR_FILE_INREPO); | |
ab43ad2f | 330 | if (error < 0) |
ee1f0b1a RB |
331 | goto cleanup; |
332 | ||
0cfcff5d RB |
333 | info.repo = repo; |
334 | info.files = files; | |
335 | error = git_path_walk_up(&dir, workdir, push_one_attr, &info); | |
ab43ad2f | 336 | if (error < 0) |
ee1f0b1a RB |
337 | goto cleanup; |
338 | ||
95dfb031 RB |
339 | if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) { |
340 | error = push_attrs( | |
341 | repo, files, NULL, git_repository_attr_cache(repo)->cfg_attr_file); | |
342 | if (error < 0) | |
343 | goto cleanup; | |
ee1f0b1a RB |
344 | } |
345 | ||
95dfb031 RB |
346 | error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM); |
347 | if (!error) | |
348 | error = push_attrs(repo, files, NULL, dir.ptr); | |
349 | else if (error == GIT_ENOTFOUND) | |
350 | error = 0; | |
ee1f0b1a RB |
351 | |
352 | cleanup: | |
ab43ad2f | 353 | if (error < 0) |
ee1f0b1a | 354 | git_vector_free(files); |
ee1f0b1a RB |
355 | git_buf_free(&dir); |
356 | ||
357 | return error; | |
358 | } | |
359 | ||
360 | ||
df743c7d | 361 | int git_attr_cache__init(git_repository *repo) |
ee1f0b1a | 362 | { |
7784bcbb | 363 | int ret; |
95dfb031 RB |
364 | git_attr_cache *cache = git_repository_attr_cache(repo); |
365 | git_config *cfg; | |
73b51450 RB |
366 | |
367 | if (cache->initialized) | |
ab43ad2f | 368 | return 0; |
73b51450 | 369 | |
95dfb031 | 370 | /* cache config settings for attributes and ignores */ |
7784bcbb | 371 | if (git_repository_config__weakptr(&cfg, repo) < 0) |
95dfb031 | 372 | return -1; |
7784bcbb RB |
373 | |
374 | ret = git_config_get_string(cfg, GIT_ATTR_CONFIG, &cache->cfg_attr_file); | |
375 | if (ret < 0 && ret != GIT_ENOTFOUND) | |
376 | return ret; | |
377 | ||
378 | ret = git_config_get_string(cfg, GIT_IGNORE_CONFIG, &cache->cfg_excl_file); | |
379 | if (ret < 0 && ret != GIT_ENOTFOUND) | |
380 | return ret; | |
381 | ||
382 | giterr_clear(); | |
95dfb031 RB |
383 | |
384 | /* allocate hashtable for attribute and ignore file contents */ | |
73b51450 | 385 | if (cache->files == NULL) { |
01fed0a8 RB |
386 | cache->files = git_khash_str_alloc(); |
387 | GITERR_CHECK_ALLOC(cache->files); | |
73b51450 RB |
388 | } |
389 | ||
95dfb031 | 390 | /* allocate hashtable for attribute macros */ |
73b51450 | 391 | if (cache->macros == NULL) { |
01fed0a8 RB |
392 | cache->macros = git_khash_str_alloc(); |
393 | GITERR_CHECK_ALLOC(cache->macros); | |
ee1f0b1a | 394 | } |
73b51450 | 395 | |
19fa2bc1 RB |
396 | /* allocate string pool */ |
397 | if (git_pool_init(&cache->pool, 1, 0) < 0) | |
398 | return -1; | |
399 | ||
73b51450 RB |
400 | cache->initialized = 1; |
401 | ||
402 | /* insert default macros */ | |
0d0fa7c3 | 403 | return git_attr_add_macro(repo, "binary", "-diff -crlf -text"); |
73b51450 RB |
404 | } |
405 | ||
73b51450 RB |
406 | void git_attr_cache_flush( |
407 | git_repository *repo) | |
408 | { | |
19fa2bc1 | 409 | git_attr_cache *cache; |
95dfb031 | 410 | |
73b51450 RB |
411 | if (!repo) |
412 | return; | |
413 | ||
19fa2bc1 | 414 | cache = git_repository_attr_cache(repo); |
73b51450 | 415 | |
19fa2bc1 RB |
416 | if (cache->files != NULL) { |
417 | git_attr_file *file; | |
01fed0a8 RB |
418 | |
419 | git_khash_str_foreach_value(cache->files, file, { | |
420 | git_attr_file__free(file); | |
421 | }); | |
422 | ||
423 | git_khash_str_free(cache->files); | |
73b51450 RB |
424 | } |
425 | ||
19fa2bc1 | 426 | if (cache->macros != NULL) { |
73b51450 RB |
427 | git_attr_rule *rule; |
428 | ||
01fed0a8 RB |
429 | git_khash_str_foreach_value(cache->macros, rule, { |
430 | git_attr_rule__free(rule); | |
431 | }); | |
432 | ||
433 | git_khash_str_free(cache->macros); | |
73b51450 RB |
434 | } |
435 | ||
19fa2bc1 RB |
436 | git_pool_clear(&cache->pool); |
437 | ||
438 | cache->initialized = 0; | |
ee1f0b1a | 439 | } |
df743c7d RB |
440 | |
441 | int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro) | |
442 | { | |
01fed0a8 RB |
443 | git_khash_str *macros = git_repository_attr_cache(repo)->macros; |
444 | int error; | |
445 | ||
ab43ad2f | 446 | /* TODO: generate warning log if (macro->assigns.length == 0) */ |
df743c7d | 447 | if (macro->assigns.length == 0) |
ab43ad2f | 448 | return 0; |
df743c7d | 449 | |
01fed0a8 RB |
450 | git_khash_str_insert(macros, macro->match.pattern, macro, error); |
451 | return (error < 0) ? -1 : 0; | |
df743c7d | 452 | } |
01fed0a8 RB |
453 | |
454 | git_attr_rule *git_attr_cache__lookup_macro( | |
455 | git_repository *repo, const char *name) | |
456 | { | |
457 | git_khash_str *macros = git_repository_attr_cache(repo)->macros; | |
458 | khiter_t pos; | |
459 | ||
460 | pos = git_khash_str_lookup_index(macros, name); | |
461 | ||
462 | if (!git_khash_str_valid_index(macros, pos)) | |
463 | return NULL; | |
464 | ||
465 | return (git_attr_rule *)git_khash_str_value_at(macros, pos); | |
466 | } | |
467 |