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