]>
Commit | Line | Data |
---|---|---|
157cef10 | 1 | #include "common.h" |
73b51450 | 2 | #include "repository.h" |
ee1f0b1a RB |
3 | #include "fileops.h" |
4 | #include "config.h" | |
5540d947 RB |
5 | #include "attr.h" |
6 | #include "ignore.h" | |
c07d9c95 | 7 | #include "git2/oid.h" |
ee1f0b1a RB |
8 | #include <ctype.h> |
9 | ||
c2b67043 | 10 | GIT__USE_STRMAP; |
01fed0a8 | 11 | |
0c9eacf3 VM |
12 | const char *git_attr__true = "[internal]__TRUE__"; |
13 | const char *git_attr__false = "[internal]__FALSE__"; | |
14 | const char *git_attr__unset = "[internal]__UNSET__"; | |
15 | ||
16 | git_attr_t git_attr_value(const char *attr) | |
17 | { | |
18 | if (attr == NULL || attr == git_attr__unset) | |
19 | return GIT_ATTR_UNSPECIFIED_T; | |
20 | ||
21 | if (attr == git_attr__true) | |
22 | return GIT_ATTR_TRUE_T; | |
23 | ||
24 | if (attr == git_attr__false) | |
25 | return GIT_ATTR_FALSE_T; | |
26 | ||
27 | return GIT_ATTR_VALUE_T; | |
28 | } | |
29 | ||
ee1f0b1a | 30 | static int collect_attr_files( |
f917481e RB |
31 | git_repository *repo, |
32 | uint32_t flags, | |
33 | const char *path, | |
34 | git_vector *files); | |
ee1f0b1a RB |
35 | |
36 | ||
37 | int git_attr_get( | |
29e948de | 38 | const char **value, |
0cb16fe9 | 39 | git_repository *repo, |
f917481e RB |
40 | uint32_t flags, |
41 | const char *pathname, | |
29e948de | 42 | const char *name) |
ee1f0b1a RB |
43 | { |
44 | int error; | |
45 | git_attr_path path; | |
46 | git_vector files = GIT_VECTOR_INIT; | |
b8457baa | 47 | size_t i, j; |
ee1f0b1a RB |
48 | git_attr_file *file; |
49 | git_attr_name attr; | |
50 | git_attr_rule *rule; | |
51 | ||
52 | *value = NULL; | |
53 | ||
d58336dd RB |
54 | if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0) |
55 | return -1; | |
56 | ||
f917481e | 57 | if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0) |
d58336dd | 58 | goto cleanup; |
ee1f0b1a RB |
59 | |
60 | attr.name = name; | |
61 | attr.name_hash = git_attr_file__name_hash(name); | |
62 | ||
63 | git_vector_foreach(&files, i, file) { | |
64 | ||
65 | git_attr_file__foreach_matching_rule(file, &path, j, rule) { | |
11d9f6b3 PK |
66 | size_t pos; |
67 | ||
68 | if (!git_vector_bsearch(&pos, &rule->assigns, &attr)) { | |
ee1f0b1a RB |
69 | *value = ((git_attr_assignment *)git_vector_get( |
70 | &rule->assigns, pos))->value; | |
d58336dd | 71 | goto cleanup; |
ee1f0b1a RB |
72 | } |
73 | } | |
74 | } | |
75 | ||
d58336dd | 76 | cleanup: |
ee1f0b1a | 77 | git_vector_free(&files); |
d58336dd | 78 | git_attr_path__free(&path); |
ee1f0b1a RB |
79 | |
80 | return error; | |
81 | } | |
82 | ||
83 | ||
84 | typedef struct { | |
85 | git_attr_name name; | |
86 | git_attr_assignment *found; | |
87 | } attr_get_many_info; | |
88 | ||
89 | int git_attr_get_many( | |
29e948de | 90 | const char **values, |
0cb16fe9 | 91 | git_repository *repo, |
f917481e RB |
92 | uint32_t flags, |
93 | const char *pathname, | |
0cb16fe9 | 94 | size_t num_attr, |
29e948de | 95 | const char **names) |
ee1f0b1a RB |
96 | { |
97 | int error; | |
98 | git_attr_path path; | |
99 | git_vector files = GIT_VECTOR_INIT; | |
b8457baa | 100 | size_t i, j, k; |
ee1f0b1a RB |
101 | git_attr_file *file; |
102 | git_attr_rule *rule; | |
103 | attr_get_many_info *info = NULL; | |
104 | size_t num_found = 0; | |
105 | ||
d58336dd RB |
106 | if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0) |
107 | return -1; | |
108 | ||
f917481e | 109 | if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0) |
d58336dd | 110 | goto cleanup; |
ee1f0b1a | 111 | |
0d0fa7c3 RB |
112 | info = git__calloc(num_attr, sizeof(attr_get_many_info)); |
113 | GITERR_CHECK_ALLOC(info); | |
ee1f0b1a RB |
114 | |
115 | git_vector_foreach(&files, i, file) { | |
116 | ||
117 | git_attr_file__foreach_matching_rule(file, &path, j, rule) { | |
118 | ||
119 | for (k = 0; k < num_attr; k++) { | |
11d9f6b3 | 120 | size_t pos; |
ee1f0b1a RB |
121 | |
122 | if (info[k].found != NULL) /* already found assignment */ | |
123 | continue; | |
124 | ||
125 | if (!info[k].name.name) { | |
126 | info[k].name.name = names[k]; | |
127 | info[k].name.name_hash = git_attr_file__name_hash(names[k]); | |
128 | } | |
129 | ||
11d9f6b3 | 130 | if (!git_vector_bsearch(&pos, &rule->assigns, &info[k].name)) { |
ee1f0b1a RB |
131 | info[k].found = (git_attr_assignment *) |
132 | git_vector_get(&rule->assigns, pos); | |
133 | values[k] = info[k].found->value; | |
134 | ||
135 | if (++num_found == num_attr) | |
136 | goto cleanup; | |
137 | } | |
138 | } | |
139 | } | |
140 | } | |
141 | ||
974774c7 RB |
142 | for (k = 0; k < num_attr; k++) { |
143 | if (!info[k].found) | |
144 | values[k] = NULL; | |
145 | } | |
146 | ||
ee1f0b1a RB |
147 | cleanup: |
148 | git_vector_free(&files); | |
d58336dd | 149 | git_attr_path__free(&path); |
ee1f0b1a RB |
150 | git__free(info); |
151 | ||
152 | return error; | |
153 | } | |
154 | ||
155 | ||
156 | int git_attr_foreach( | |
0cb16fe9 | 157 | git_repository *repo, |
f917481e RB |
158 | uint32_t flags, |
159 | const char *pathname, | |
ee1f0b1a RB |
160 | int (*callback)(const char *name, const char *value, void *payload), |
161 | void *payload) | |
162 | { | |
163 | int error; | |
164 | git_attr_path path; | |
165 | git_vector files = GIT_VECTOR_INIT; | |
b8457baa | 166 | size_t i, j, k; |
ee1f0b1a RB |
167 | git_attr_file *file; |
168 | git_attr_rule *rule; | |
169 | git_attr_assignment *assign; | |
c2b67043 | 170 | git_strmap *seen = NULL; |
ee1f0b1a | 171 | |
d58336dd RB |
172 | if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0) |
173 | return -1; | |
174 | ||
f917481e | 175 | if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0) |
d58336dd | 176 | goto cleanup; |
ee1f0b1a | 177 | |
c2b67043 | 178 | seen = git_strmap_alloc(); |
0d0fa7c3 | 179 | GITERR_CHECK_ALLOC(seen); |
ee1f0b1a RB |
180 | |
181 | git_vector_foreach(&files, i, file) { | |
182 | ||
183 | git_attr_file__foreach_matching_rule(file, &path, j, rule) { | |
184 | ||
185 | git_vector_foreach(&rule->assigns, k, assign) { | |
186 | /* skip if higher priority assignment was already seen */ | |
c2b67043 | 187 | if (git_strmap_exists(seen, assign->name)) |
ee1f0b1a RB |
188 | continue; |
189 | ||
c2b67043 | 190 | git_strmap_insert(seen, assign->name, assign, error); |
5dca2010 RB |
191 | if (error < 0) |
192 | goto cleanup; | |
ee1f0b1a | 193 | |
5dca2010 RB |
194 | error = callback(assign->name, assign->value, payload); |
195 | if (error) { | |
96869a4e | 196 | error = giterr_user_cancel(); |
ee1f0b1a | 197 | goto cleanup; |
5dca2010 | 198 | } |
ee1f0b1a RB |
199 | } |
200 | } | |
201 | } | |
202 | ||
203 | cleanup: | |
c2b67043 | 204 | git_strmap_free(seen); |
ee1f0b1a | 205 | git_vector_free(&files); |
d58336dd | 206 | git_attr_path__free(&path); |
ee1f0b1a | 207 | |
ee1f0b1a RB |
208 | return error; |
209 | } | |
210 | ||
211 | ||
73b51450 RB |
212 | int git_attr_add_macro( |
213 | git_repository *repo, | |
214 | const char *name, | |
215 | const char *values) | |
216 | { | |
217 | int error; | |
218 | git_attr_rule *macro = NULL; | |
19fa2bc1 | 219 | git_pool *pool; |
73b51450 | 220 | |
0d0fa7c3 RB |
221 | if (git_attr_cache__init(repo) < 0) |
222 | return -1; | |
73b51450 RB |
223 | |
224 | macro = git__calloc(1, sizeof(git_attr_rule)); | |
0d0fa7c3 | 225 | GITERR_CHECK_ALLOC(macro); |
73b51450 | 226 | |
19fa2bc1 RB |
227 | pool = &git_repository_attr_cache(repo)->pool; |
228 | ||
229 | macro->match.pattern = git_pool_strdup(pool, name); | |
0d0fa7c3 | 230 | GITERR_CHECK_ALLOC(macro->match.pattern); |
73b51450 RB |
231 | |
232 | macro->match.length = strlen(macro->match.pattern); | |
233 | macro->match.flags = GIT_ATTR_FNMATCH_MACRO; | |
234 | ||
19fa2bc1 | 235 | error = git_attr_assignment__parse(repo, pool, ¯o->assigns, &values); |
73b51450 | 236 | |
0d0fa7c3 | 237 | if (!error) |
73b51450 RB |
238 | error = git_attr_cache__insert_macro(repo, macro); |
239 | ||
0d0fa7c3 | 240 | if (error < 0) |
73b51450 | 241 | git_attr_rule__free(macro); |
73b51450 RB |
242 | |
243 | return error; | |
244 | } | |
245 | ||
f917481e RB |
246 | bool git_attr_cache__is_cached( |
247 | git_repository *repo, git_attr_file_source source, const char *path) | |
e8c96ed2 | 248 | { |
f917481e | 249 | git_buf cache_key = GIT_BUF_INIT; |
c2b67043 | 250 | git_strmap *files = git_repository_attr_cache(repo)->files; |
f917481e RB |
251 | const char *workdir = git_repository_workdir(repo); |
252 | bool rval; | |
253 | ||
254 | if (workdir && git__prefixcmp(path, workdir) == 0) | |
255 | path += strlen(workdir); | |
256 | if (git_buf_printf(&cache_key, "%d#%s", (int)source, path) < 0) | |
257 | return false; | |
258 | ||
259 | rval = git_strmap_exists(files, git_buf_cstr(&cache_key)); | |
260 | ||
261 | git_buf_free(&cache_key); | |
262 | ||
263 | return rval; | |
264 | } | |
265 | ||
dc13f1f7 RB |
266 | static int load_attr_file( |
267 | const char **data, | |
c1f61af6 | 268 | git_futils_filestamp *stamp, |
dc13f1f7 | 269 | const char *filename) |
f917481e RB |
270 | { |
271 | int error; | |
272 | git_buf content = GIT_BUF_INIT; | |
273 | ||
c1f61af6 | 274 | error = git_futils_filestamp_check(stamp, filename); |
744cc03e RB |
275 | if (error < 0) |
276 | return error; | |
f917481e | 277 | |
744cc03e RB |
278 | /* if error == 0, then file is up to date. By returning GIT_ENOTFOUND, |
279 | * we tell the caller not to reparse this file... | |
280 | */ | |
281 | if (!error) | |
904b67e6 | 282 | return GIT_ENOTFOUND; |
dc13f1f7 | 283 | |
744cc03e | 284 | error = git_futils_readbuffer(&content, filename); |
37d91686 RB |
285 | if (error < 0) { |
286 | /* convert error into ENOTFOUND so failed permissions / invalid | |
287 | * file type don't actually stop the operation in progress. | |
288 | */ | |
289 | return GIT_ENOTFOUND; | |
290 | ||
291 | /* TODO: once warnings are available, issue a warning callback */ | |
292 | } | |
dc13f1f7 | 293 | |
dc13f1f7 RB |
294 | *data = git_buf_detach(&content); |
295 | ||
296 | return 0; | |
f917481e | 297 | } |
01fed0a8 | 298 | |
f917481e | 299 | static int load_attr_blob_from_index( |
dc13f1f7 RB |
300 | const char **content, |
301 | git_blob **blob, | |
302 | git_repository *repo, | |
303 | const git_oid *old_oid, | |
304 | const char *relfile) | |
f917481e RB |
305 | { |
306 | int error; | |
11d9f6b3 | 307 | size_t pos; |
f917481e | 308 | git_index *index; |
f45d51ff | 309 | const git_index_entry *entry; |
f917481e RB |
310 | |
311 | if ((error = git_repository_index__weakptr(&index, repo)) < 0 || | |
11d9f6b3 | 312 | (error = git_index_find(&pos, index, relfile)) < 0) |
f917481e | 313 | return error; |
01fed0a8 | 314 | |
11d9f6b3 | 315 | entry = git_index_get_byindex(index, pos); |
f917481e | 316 | |
b7f167da | 317 | if (old_oid && git_oid__cmp(old_oid, &entry->oid) == 0) |
904b67e6 | 318 | return GIT_ENOTFOUND; |
dc13f1f7 RB |
319 | |
320 | if ((error = git_blob_lookup(blob, repo, &entry->oid)) < 0) | |
321 | return error; | |
322 | ||
323 | *content = git_blob_rawcontent(*blob); | |
324 | return 0; | |
325 | } | |
326 | ||
327 | static int load_attr_from_cache( | |
328 | git_attr_file **file, | |
329 | git_attr_cache *cache, | |
330 | git_attr_file_source source, | |
331 | const char *relative_path) | |
332 | { | |
333 | git_buf cache_key = GIT_BUF_INIT; | |
334 | khiter_t cache_pos; | |
335 | ||
336 | *file = NULL; | |
337 | ||
338 | if (!cache || !cache->files) | |
339 | return 0; | |
340 | ||
341 | if (git_buf_printf(&cache_key, "%d#%s", (int)source, relative_path) < 0) | |
342 | return -1; | |
343 | ||
344 | cache_pos = git_strmap_lookup_index(cache->files, cache_key.ptr); | |
345 | ||
346 | git_buf_free(&cache_key); | |
347 | ||
348 | if (git_strmap_valid_index(cache->files, cache_pos)) | |
349 | *file = git_strmap_value_at(cache->files, cache_pos); | |
350 | ||
351 | return 0; | |
e8c96ed2 | 352 | } |
73b51450 | 353 | |
f917481e | 354 | int git_attr_cache__internal_file( |
b6c93aef | 355 | git_repository *repo, |
b6c93aef | 356 | const char *filename, |
f917481e | 357 | git_attr_file **file) |
b6c93aef | 358 | { |
f917481e | 359 | int error = 0; |
95dfb031 | 360 | git_attr_cache *cache = git_repository_attr_cache(repo); |
f917481e | 361 | khiter_t cache_pos = git_strmap_lookup_index(cache->files, filename); |
b6c93aef | 362 | |
f917481e RB |
363 | if (git_strmap_valid_index(cache->files, cache_pos)) { |
364 | *file = git_strmap_value_at(cache->files, cache_pos); | |
0d0fa7c3 | 365 | return 0; |
b6c93aef RB |
366 | } |
367 | ||
f917481e | 368 | if (git_attr_file__new(file, 0, filename, &cache->pool) < 0) |
0d0fa7c3 | 369 | return -1; |
b6c93aef | 370 | |
f917481e RB |
371 | git_strmap_insert(cache->files, (*file)->key + 2, *file, error); |
372 | if (error > 0) | |
373 | error = 0; | |
b6c93aef | 374 | |
b6c93aef RB |
375 | return error; |
376 | } | |
377 | ||
df743c7d | 378 | int git_attr_cache__push_file( |
ee1f0b1a | 379 | git_repository *repo, |
f917481e RB |
380 | const char *base, |
381 | const char *filename, | |
382 | git_attr_file_source source, | |
383 | git_attr_file_parser parse, | |
ec40b7f9 | 384 | void* parsedata, |
f917481e | 385 | git_vector *stack) |
ee1f0b1a | 386 | { |
f917481e | 387 | int error = 0; |
ee1f0b1a | 388 | git_buf path = GIT_BUF_INIT; |
f917481e RB |
389 | const char *workdir = git_repository_workdir(repo); |
390 | const char *relfile, *content = NULL; | |
391 | git_attr_cache *cache = git_repository_attr_cache(repo); | |
62a1f713 | 392 | git_attr_file *file = NULL; |
f917481e | 393 | git_blob *blob = NULL; |
c1f61af6 | 394 | git_futils_filestamp stamp; |
f917481e RB |
395 | |
396 | assert(filename && stack); | |
ee1f0b1a | 397 | |
f917481e RB |
398 | /* join base and path as needed */ |
399 | if (base != NULL && git_path_root(filename) < 0) { | |
0d0fa7c3 RB |
400 | if (git_buf_joinpath(&path, base, filename) < 0) |
401 | return -1; | |
1dbcc9fc RB |
402 | filename = path.ptr; |
403 | } | |
ee1f0b1a | 404 | |
f917481e RB |
405 | relfile = filename; |
406 | if (workdir && git__prefixcmp(relfile, workdir) == 0) | |
407 | relfile += strlen(workdir); | |
62a1f713 | 408 | |
f917481e | 409 | /* check cache */ |
dc13f1f7 RB |
410 | if (load_attr_from_cache(&file, cache, source, relfile) < 0) |
411 | return -1; | |
ee1f0b1a | 412 | |
dc13f1f7 | 413 | /* if not in cache, load data, parse, and cache */ |
f917481e | 414 | |
dc13f1f7 | 415 | if (source == GIT_ATTR_FILE_FROM_FILE) { |
c1f61af6 | 416 | git_futils_filestamp_set( |
c8b511f3 | 417 | &stamp, file ? &file->cache_data.stamp : NULL); |
f917481e | 418 | |
c8b511f3 | 419 | error = load_attr_file(&content, &stamp, filename); |
dc13f1f7 RB |
420 | } else { |
421 | error = load_attr_blob_from_index(&content, &blob, | |
422 | repo, file ? &file->cache_data.oid : NULL, relfile); | |
f917481e RB |
423 | } |
424 | ||
f917481e RB |
425 | if (error) { |
426 | /* not finding a file is not an error for this function */ | |
904b67e6 | 427 | if (error == GIT_ENOTFOUND) { |
f917481e RB |
428 | giterr_clear(); |
429 | error = 0; | |
430 | } | |
431 | goto finish; | |
432 | } | |
433 | ||
2a99df69 RB |
434 | /* if we got here, we have to parse and/or reparse the file */ |
435 | if (file) | |
436 | git_attr_file__clear_rules(file); | |
437 | else { | |
438 | error = git_attr_file__new(&file, source, relfile, &cache->pool); | |
439 | if (error < 0) | |
440 | goto finish; | |
441 | } | |
b709e951 | 442 | |
ec40b7f9 | 443 | if (parse && (error = parse(repo, parsedata, content, file)) < 0) |
f917481e RB |
444 | goto finish; |
445 | ||
3f035860 | 446 | git_strmap_insert(cache->files, file->key, file, error); //-V595 |
f917481e RB |
447 | if (error > 0) |
448 | error = 0; | |
449 | ||
dc13f1f7 RB |
450 | /* remember "cache buster" file signature */ |
451 | if (blob) | |
452 | git_oid_cpy(&file->cache_data.oid, git_object_id((git_object *)blob)); | |
453 | else | |
c1f61af6 | 454 | git_futils_filestamp_set(&file->cache_data.stamp, &stamp); |
dc13f1f7 | 455 | |
f917481e RB |
456 | finish: |
457 | /* push file onto vector if we found one*/ | |
0d0fa7c3 | 458 | if (!error && file != NULL) |
df743c7d | 459 | error = git_vector_insert(stack, file); |
ee1f0b1a | 460 | |
f917481e RB |
461 | if (error != 0) |
462 | git_attr_file__free(file); | |
463 | ||
464 | if (blob) | |
465 | git_blob_free(blob); | |
466 | else | |
467 | git__free((void *)content); | |
468 | ||
ee1f0b1a | 469 | git_buf_free(&path); |
f917481e | 470 | |
ee1f0b1a RB |
471 | return error; |
472 | } | |
473 | ||
f917481e | 474 | #define push_attr_file(R,S,B,F) \ |
ec40b7f9 | 475 | git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,git_attr_file__parse_buffer,NULL,(S)) |
ee1f0b1a | 476 | |
0cfcff5d RB |
477 | typedef struct { |
478 | git_repository *repo; | |
f917481e RB |
479 | uint32_t flags; |
480 | const char *workdir; | |
481 | git_index *index; | |
0cfcff5d RB |
482 | git_vector *files; |
483 | } attr_walk_up_info; | |
484 | ||
f917481e RB |
485 | int git_attr_cache__decide_sources( |
486 | uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs) | |
487 | { | |
488 | int count = 0; | |
489 | ||
490 | switch (flags & 0x03) { | |
491 | case GIT_ATTR_CHECK_FILE_THEN_INDEX: | |
492 | if (has_wd) | |
493 | srcs[count++] = GIT_ATTR_FILE_FROM_FILE; | |
494 | if (has_index) | |
495 | srcs[count++] = GIT_ATTR_FILE_FROM_INDEX; | |
496 | break; | |
497 | case GIT_ATTR_CHECK_INDEX_THEN_FILE: | |
498 | if (has_index) | |
499 | srcs[count++] = GIT_ATTR_FILE_FROM_INDEX; | |
500 | if (has_wd) | |
501 | srcs[count++] = GIT_ATTR_FILE_FROM_FILE; | |
502 | break; | |
503 | case GIT_ATTR_CHECK_INDEX_ONLY: | |
504 | if (has_index) | |
505 | srcs[count++] = GIT_ATTR_FILE_FROM_INDEX; | |
506 | break; | |
507 | } | |
508 | ||
509 | return count; | |
510 | } | |
511 | ||
0cfcff5d RB |
512 | static int push_one_attr(void *ref, git_buf *path) |
513 | { | |
f917481e | 514 | int error = 0, n_src, i; |
0cfcff5d | 515 | attr_walk_up_info *info = (attr_walk_up_info *)ref; |
f917481e RB |
516 | git_attr_file_source src[2]; |
517 | ||
518 | n_src = git_attr_cache__decide_sources( | |
519 | info->flags, info->workdir != NULL, info->index != NULL, src); | |
520 | ||
521 | for (i = 0; !error && i < n_src; ++i) | |
522 | error = git_attr_cache__push_file( | |
523 | info->repo, path->ptr, GIT_ATTR_FILE, src[i], | |
ec40b7f9 | 524 | git_attr_file__parse_buffer, NULL, info->files); |
f917481e RB |
525 | |
526 | return error; | |
0cfcff5d RB |
527 | } |
528 | ||
ee1f0b1a | 529 | static int collect_attr_files( |
f917481e RB |
530 | git_repository *repo, |
531 | uint32_t flags, | |
532 | const char *path, | |
533 | git_vector *files) | |
ee1f0b1a | 534 | { |
0d0fa7c3 | 535 | int error; |
ee1f0b1a | 536 | git_buf dir = GIT_BUF_INIT; |
ee1f0b1a | 537 | const char *workdir = git_repository_workdir(repo); |
0cfcff5d | 538 | attr_walk_up_info info; |
ee1f0b1a | 539 | |
ab43ad2f RB |
540 | if (git_attr_cache__init(repo) < 0 || |
541 | git_vector_init(files, 4, NULL) < 0) | |
542 | return -1; | |
ee1f0b1a | 543 | |
cba285d3 RB |
544 | /* Resolve path in a non-bare repo */ |
545 | if (workdir != NULL) | |
f917481e RB |
546 | error = git_path_find_dir(&dir, path, workdir); |
547 | else | |
cba285d3 | 548 | error = git_path_dirname_r(&dir, path); |
ab43ad2f | 549 | if (error < 0) |
ee1f0b1a RB |
550 | goto cleanup; |
551 | ||
ee1f0b1a RB |
552 | /* in precendence order highest to lowest: |
553 | * - $GIT_DIR/info/attributes | |
554 | * - path components with .gitattributes | |
555 | * - config core.attributesfile | |
556 | * - $GIT_PREFIX/etc/gitattributes | |
557 | */ | |
558 | ||
f917481e | 559 | error = push_attr_file( |
95dfb031 | 560 | repo, files, git_repository_path(repo), GIT_ATTR_FILE_INREPO); |
ab43ad2f | 561 | if (error < 0) |
ee1f0b1a RB |
562 | goto cleanup; |
563 | ||
f917481e RB |
564 | info.repo = repo; |
565 | info.flags = flags; | |
566 | info.workdir = workdir; | |
567 | if (git_repository_index__weakptr(&info.index, repo) < 0) | |
568 | giterr_clear(); /* no error even if there is no index */ | |
0cfcff5d | 569 | info.files = files; |
f917481e | 570 | |
0cfcff5d | 571 | error = git_path_walk_up(&dir, workdir, push_one_attr, &info); |
ab43ad2f | 572 | if (error < 0) |
ee1f0b1a RB |
573 | goto cleanup; |
574 | ||
95dfb031 | 575 | if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) { |
f917481e | 576 | error = push_attr_file( |
95dfb031 RB |
577 | repo, files, NULL, git_repository_attr_cache(repo)->cfg_attr_file); |
578 | if (error < 0) | |
579 | goto cleanup; | |
ee1f0b1a RB |
580 | } |
581 | ||
f917481e RB |
582 | if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) { |
583 | error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM); | |
584 | if (!error) | |
585 | error = push_attr_file(repo, files, NULL, dir.ptr); | |
6ac724af RB |
586 | else if (error == GIT_ENOTFOUND) { |
587 | giterr_clear(); | |
f917481e | 588 | error = 0; |
6ac724af | 589 | } |
f917481e | 590 | } |
ee1f0b1a RB |
591 | |
592 | cleanup: | |
ab43ad2f | 593 | if (error < 0) |
ee1f0b1a | 594 | git_vector_free(files); |
ee1f0b1a RB |
595 | git_buf_free(&dir); |
596 | ||
597 | return error; | |
598 | } | |
599 | ||
5540d947 | 600 | static int attr_cache__lookup_path( |
7a5ee3dc | 601 | char **out, git_config *cfg, const char *key, const char *fallback) |
7fbca880 | 602 | { |
5540d947 RB |
603 | git_buf buf = GIT_BUF_INIT; |
604 | int error; | |
9f77b3f6 | 605 | const git_config_entry *entry = NULL; |
7fbca880 | 606 | |
7a5ee3dc RB |
607 | *out = NULL; |
608 | ||
9f77b3f6 RB |
609 | if ((error = git_config__lookup_entry(&entry, cfg, key, false)) < 0) |
610 | return error; | |
611 | ||
612 | if (entry) { | |
613 | const char *cfgval = entry->value; | |
7a5ee3dc RB |
614 | |
615 | /* expand leading ~/ as needed */ | |
616 | if (cfgval && cfgval[0] == '~' && cfgval[1] == '/' && | |
617 | !git_futils_find_global_file(&buf, &cfgval[2])) | |
618 | *out = git_buf_detach(&buf); | |
619 | else if (cfgval) | |
620 | *out = git__strdup(cfgval); | |
7fbca880 | 621 | |
5540d947 | 622 | } |
9f77b3f6 RB |
623 | else if (!git_futils_find_xdg_file(&buf, fallback)) |
624 | *out = git_buf_detach(&buf); | |
5540d947 | 625 | |
7a5ee3dc RB |
626 | git_buf_free(&buf); |
627 | ||
5540d947 | 628 | return error; |
7fbca880 | 629 | } |
ee1f0b1a | 630 | |
df743c7d | 631 | int git_attr_cache__init(git_repository *repo) |
ee1f0b1a | 632 | { |
7784bcbb | 633 | int ret; |
95dfb031 RB |
634 | git_attr_cache *cache = git_repository_attr_cache(repo); |
635 | git_config *cfg; | |
73b51450 RB |
636 | |
637 | if (cache->initialized) | |
ab43ad2f | 638 | return 0; |
73b51450 | 639 | |
95dfb031 | 640 | /* cache config settings for attributes and ignores */ |
7784bcbb | 641 | if (git_repository_config__weakptr(&cfg, repo) < 0) |
95dfb031 | 642 | return -1; |
7784bcbb | 643 | |
5540d947 RB |
644 | ret = attr_cache__lookup_path( |
645 | &cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG, GIT_ATTR_FILE_XDG); | |
646 | if (ret < 0) | |
7784bcbb RB |
647 | return ret; |
648 | ||
5540d947 RB |
649 | ret = attr_cache__lookup_path( |
650 | &cache->cfg_excl_file, cfg, GIT_IGNORE_CONFIG, GIT_IGNORE_FILE_XDG); | |
651 | if (ret < 0) | |
7784bcbb | 652 | return ret; |
95dfb031 RB |
653 | |
654 | /* allocate hashtable for attribute and ignore file contents */ | |
73b51450 | 655 | if (cache->files == NULL) { |
c2b67043 | 656 | cache->files = git_strmap_alloc(); |
01fed0a8 | 657 | GITERR_CHECK_ALLOC(cache->files); |
73b51450 RB |
658 | } |
659 | ||
95dfb031 | 660 | /* allocate hashtable for attribute macros */ |
73b51450 | 661 | if (cache->macros == NULL) { |
c2b67043 | 662 | cache->macros = git_strmap_alloc(); |
01fed0a8 | 663 | GITERR_CHECK_ALLOC(cache->macros); |
ee1f0b1a | 664 | } |
73b51450 | 665 | |
19fa2bc1 RB |
666 | /* allocate string pool */ |
667 | if (git_pool_init(&cache->pool, 1, 0) < 0) | |
668 | return -1; | |
669 | ||
73b51450 RB |
670 | cache->initialized = 1; |
671 | ||
672 | /* insert default macros */ | |
0d0fa7c3 | 673 | return git_attr_add_macro(repo, "binary", "-diff -crlf -text"); |
73b51450 RB |
674 | } |
675 | ||
73b51450 RB |
676 | void git_attr_cache_flush( |
677 | git_repository *repo) | |
678 | { | |
19fa2bc1 | 679 | git_attr_cache *cache; |
95dfb031 | 680 | |
73b51450 RB |
681 | if (!repo) |
682 | return; | |
683 | ||
19fa2bc1 | 684 | cache = git_repository_attr_cache(repo); |
73b51450 | 685 | |
19fa2bc1 RB |
686 | if (cache->files != NULL) { |
687 | git_attr_file *file; | |
01fed0a8 | 688 | |
c2b67043 | 689 | git_strmap_foreach_value(cache->files, file, { |
01fed0a8 RB |
690 | git_attr_file__free(file); |
691 | }); | |
692 | ||
c2b67043 | 693 | git_strmap_free(cache->files); |
73b51450 RB |
694 | } |
695 | ||
19fa2bc1 | 696 | if (cache->macros != NULL) { |
73b51450 RB |
697 | git_attr_rule *rule; |
698 | ||
c2b67043 | 699 | git_strmap_foreach_value(cache->macros, rule, { |
01fed0a8 RB |
700 | git_attr_rule__free(rule); |
701 | }); | |
702 | ||
c2b67043 | 703 | git_strmap_free(cache->macros); |
73b51450 RB |
704 | } |
705 | ||
19fa2bc1 RB |
706 | git_pool_clear(&cache->pool); |
707 | ||
7a5ee3dc RB |
708 | git__free(cache->cfg_attr_file); |
709 | cache->cfg_attr_file = NULL; | |
710 | ||
711 | git__free(cache->cfg_excl_file); | |
712 | cache->cfg_excl_file = NULL; | |
713 | ||
19fa2bc1 | 714 | cache->initialized = 0; |
ee1f0b1a | 715 | } |
df743c7d RB |
716 | |
717 | int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro) | |
718 | { | |
c2b67043 | 719 | git_strmap *macros = git_repository_attr_cache(repo)->macros; |
01fed0a8 RB |
720 | int error; |
721 | ||
ab43ad2f | 722 | /* TODO: generate warning log if (macro->assigns.length == 0) */ |
df743c7d | 723 | if (macro->assigns.length == 0) |
ab43ad2f | 724 | return 0; |
df743c7d | 725 | |
c2b67043 | 726 | git_strmap_insert(macros, macro->match.pattern, macro, error); |
01fed0a8 | 727 | return (error < 0) ? -1 : 0; |
df743c7d | 728 | } |
01fed0a8 RB |
729 | |
730 | git_attr_rule *git_attr_cache__lookup_macro( | |
731 | git_repository *repo, const char *name) | |
732 | { | |
c2b67043 | 733 | git_strmap *macros = git_repository_attr_cache(repo)->macros; |
01fed0a8 RB |
734 | khiter_t pos; |
735 | ||
c2b67043 | 736 | pos = git_strmap_lookup_index(macros, name); |
01fed0a8 | 737 | |
c2b67043 | 738 | if (!git_strmap_valid_index(macros, pos)) |
01fed0a8 RB |
739 | return NULL; |
740 | ||
c2b67043 | 741 | return (git_attr_rule *)git_strmap_value_at(macros, pos); |
01fed0a8 RB |
742 | } |
743 |