]> git.proxmox.com Git - libgit2.git/blame - src/attr.c
Improve GIT_EUSER handling
[libgit2.git] / src / attr.c
CommitLineData
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 10GIT__USE_STRMAP;
01fed0a8 11
0c9eacf3
VM
12const char *git_attr__true = "[internal]__TRUE__";
13const char *git_attr__false = "[internal]__FALSE__";
14const char *git_attr__unset = "[internal]__UNSET__";
15
16git_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 30static 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
37int 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 76cleanup:
ee1f0b1a 77 git_vector_free(&files);
d58336dd 78 git_attr_path__free(&path);
ee1f0b1a
RB
79
80 return error;
81}
82
83
84typedef struct {
85 git_attr_name name;
86 git_attr_assignment *found;
87} attr_get_many_info;
88
89int 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
147cleanup:
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
156int 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
203cleanup:
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
212int 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, &macro->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
246bool 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
266static 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 299static 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
327static 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 354int 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 378int 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
456finish:
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
477typedef 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
485int 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
512static 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 529static 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 600static 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 631int 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
676void 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
717int 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
730git_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