]> git.proxmox.com Git - libgit2.git/blame - src/attr.c
New upstream version 1.3.0+dfsg.1
[libgit2.git] / src / attr.c
CommitLineData
eae0bfdc
PP
1/*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7
8#include "attr.h"
9
73b51450 10#include "repository.h"
83634d38 11#include "sysdir.h"
ee1f0b1a 12#include "config.h"
7d490872 13#include "attr_file.h"
5540d947 14#include "ignore.h"
c07d9c95 15#include "git2/oid.h"
ee1f0b1a
RB
16#include <ctype.h>
17
0c9eacf3
VM
18const char *git_attr__true = "[internal]__TRUE__";
19const char *git_attr__false = "[internal]__FALSE__";
20const char *git_attr__unset = "[internal]__UNSET__";
21
22a2d3d5 22git_attr_value_t git_attr_value(const char *attr)
0c9eacf3
VM
23{
24 if (attr == NULL || attr == git_attr__unset)
22a2d3d5 25 return GIT_ATTR_VALUE_UNSPECIFIED;
0c9eacf3
VM
26
27 if (attr == git_attr__true)
22a2d3d5 28 return GIT_ATTR_VALUE_TRUE;
0c9eacf3
VM
29
30 if (attr == git_attr__false)
22a2d3d5 31 return GIT_ATTR_VALUE_FALSE;
0c9eacf3 32
22a2d3d5 33 return GIT_ATTR_VALUE_STRING;
0c9eacf3
VM
34}
35
ee1f0b1a 36static int collect_attr_files(
f917481e 37 git_repository *repo,
9f779aac 38 git_attr_session *attr_session,
c25aa7cd 39 git_attr_options *opts,
f917481e
RB
40 const char *path,
41 git_vector *files);
ee1f0b1a 42
40ed4990 43static void release_attr_files(git_vector *files);
ee1f0b1a 44
c25aa7cd 45int git_attr_get_ext(
29e948de 46 const char **value,
0cb16fe9 47 git_repository *repo,
c25aa7cd 48 git_attr_options *opts,
f917481e 49 const char *pathname,
29e948de 50 const char *name)
ee1f0b1a
RB
51{
52 int error;
53 git_attr_path path;
54 git_vector files = GIT_VECTOR_INIT;
b8457baa 55 size_t i, j;
ee1f0b1a
RB
56 git_attr_file *file;
57 git_attr_name attr;
58 git_attr_rule *rule;
eae0bfdc 59 git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
ee1f0b1a 60
c25aa7cd
PP
61 GIT_ASSERT_ARG(value);
62 GIT_ASSERT_ARG(repo);
63 GIT_ASSERT_ARG(name);
64 GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
3cf11eef 65
ee1f0b1a
RB
66 *value = NULL;
67
eae0bfdc
PP
68 if (git_repository_is_bare(repo))
69 dir_flag = GIT_DIR_FLAG_FALSE;
70
71 if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0)
d58336dd
RB
72 return -1;
73
c25aa7cd 74 if ((error = collect_attr_files(repo, NULL, opts, pathname, &files)) < 0)
d58336dd 75 goto cleanup;
ee1f0b1a 76
2e9d813b 77 memset(&attr, 0, sizeof(attr));
ee1f0b1a
RB
78 attr.name = name;
79 attr.name_hash = git_attr_file__name_hash(name);
80
81 git_vector_foreach(&files, i, file) {
82
83 git_attr_file__foreach_matching_rule(file, &path, j, rule) {
11d9f6b3
PK
84 size_t pos;
85
86 if (!git_vector_bsearch(&pos, &rule->assigns, &attr)) {
ee1f0b1a
RB
87 *value = ((git_attr_assignment *)git_vector_get(
88 &rule->assigns, pos))->value;
d58336dd 89 goto cleanup;
ee1f0b1a
RB
90 }
91 }
92 }
93
d58336dd 94cleanup:
40ed4990 95 release_attr_files(&files);
d58336dd 96 git_attr_path__free(&path);
ee1f0b1a
RB
97
98 return error;
99}
100
c25aa7cd
PP
101int git_attr_get(
102 const char **value,
103 git_repository *repo,
104 uint32_t flags,
105 const char *pathname,
106 const char *name)
107{
108 git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
109
110 opts.flags = flags;
111
112 return git_attr_get_ext(value, repo, &opts, pathname, name);
113}
114
ee1f0b1a
RB
115
116typedef struct {
117 git_attr_name name;
118 git_attr_assignment *found;
119} attr_get_many_info;
120
9f779aac 121int git_attr_get_many_with_session(
29e948de 122 const char **values,
0cb16fe9 123 git_repository *repo,
9f779aac 124 git_attr_session *attr_session,
c25aa7cd 125 git_attr_options *opts,
f917481e 126 const char *pathname,
0cb16fe9 127 size_t num_attr,
29e948de 128 const char **names)
ee1f0b1a
RB
129{
130 int error;
131 git_attr_path path;
132 git_vector files = GIT_VECTOR_INIT;
b8457baa 133 size_t i, j, k;
ee1f0b1a
RB
134 git_attr_file *file;
135 git_attr_rule *rule;
136 attr_get_many_info *info = NULL;
137 size_t num_found = 0;
eae0bfdc 138 git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
ee1f0b1a 139
3cf11eef
RB
140 if (!num_attr)
141 return 0;
142
c25aa7cd
PP
143 GIT_ASSERT_ARG(values);
144 GIT_ASSERT_ARG(repo);
145 GIT_ASSERT_ARG(pathname);
146 GIT_ASSERT_ARG(names);
147 GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
3cf11eef 148
eae0bfdc
PP
149 if (git_repository_is_bare(repo))
150 dir_flag = GIT_DIR_FLAG_FALSE;
151
152 if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0)
d58336dd
RB
153 return -1;
154
c25aa7cd 155 if ((error = collect_attr_files(repo, attr_session, opts, pathname, &files)) < 0)
d58336dd 156 goto cleanup;
ee1f0b1a 157
0d0fa7c3 158 info = git__calloc(num_attr, sizeof(attr_get_many_info));
ac3d33df 159 GIT_ERROR_CHECK_ALLOC(info);
ee1f0b1a
RB
160
161 git_vector_foreach(&files, i, file) {
162
163 git_attr_file__foreach_matching_rule(file, &path, j, rule) {
164
165 for (k = 0; k < num_attr; k++) {
11d9f6b3 166 size_t pos;
ee1f0b1a
RB
167
168 if (info[k].found != NULL) /* already found assignment */
169 continue;
170
171 if (!info[k].name.name) {
172 info[k].name.name = names[k];
173 info[k].name.name_hash = git_attr_file__name_hash(names[k]);
174 }
175
11d9f6b3 176 if (!git_vector_bsearch(&pos, &rule->assigns, &info[k].name)) {
ee1f0b1a
RB
177 info[k].found = (git_attr_assignment *)
178 git_vector_get(&rule->assigns, pos);
179 values[k] = info[k].found->value;
180
181 if (++num_found == num_attr)
182 goto cleanup;
183 }
184 }
185 }
186 }
187
974774c7
RB
188 for (k = 0; k < num_attr; k++) {
189 if (!info[k].found)
190 values[k] = NULL;
191 }
192
ee1f0b1a 193cleanup:
40ed4990 194 release_attr_files(&files);
d58336dd 195 git_attr_path__free(&path);
ee1f0b1a
RB
196 git__free(info);
197
198 return error;
199}
200
9f779aac
ET
201int git_attr_get_many(
202 const char **values,
203 git_repository *repo,
204 uint32_t flags,
205 const char *pathname,
206 size_t num_attr,
207 const char **names)
208{
c25aa7cd
PP
209 git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
210
211 opts.flags = flags;
212
9f779aac 213 return git_attr_get_many_with_session(
c25aa7cd
PP
214 values, repo, NULL, &opts, pathname, num_attr, names);
215}
216
217int git_attr_get_many_ext(
218 const char **values,
219 git_repository *repo,
220 git_attr_options *opts,
221 const char *pathname,
222 size_t num_attr,
223 const char **names)
224{
225 return git_attr_get_many_with_session(
226 values, repo, NULL, opts, pathname, num_attr, names);
9f779aac 227}
ee1f0b1a
RB
228
229int git_attr_foreach(
0cb16fe9 230 git_repository *repo,
f917481e
RB
231 uint32_t flags,
232 const char *pathname,
ee1f0b1a
RB
233 int (*callback)(const char *name, const char *value, void *payload),
234 void *payload)
c25aa7cd
PP
235{
236 git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
237
238 opts.flags = flags;
239
240 return git_attr_foreach_ext(repo, &opts, pathname, callback, payload);
241}
242
243int git_attr_foreach_ext(
244 git_repository *repo,
245 git_attr_options *opts,
246 const char *pathname,
247 int (*callback)(const char *name, const char *value, void *payload),
248 void *payload)
ee1f0b1a
RB
249{
250 int error;
251 git_attr_path path;
252 git_vector files = GIT_VECTOR_INIT;
b8457baa 253 size_t i, j, k;
ee1f0b1a
RB
254 git_attr_file *file;
255 git_attr_rule *rule;
256 git_attr_assignment *assign;
c2b67043 257 git_strmap *seen = NULL;
eae0bfdc 258 git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
ee1f0b1a 259
c25aa7cd
PP
260 GIT_ASSERT_ARG(repo);
261 GIT_ASSERT_ARG(callback);
262 GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
3cf11eef 263
eae0bfdc
PP
264 if (git_repository_is_bare(repo))
265 dir_flag = GIT_DIR_FLAG_FALSE;
266
267 if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0)
d58336dd
RB
268 return -1;
269
c25aa7cd 270 if ((error = collect_attr_files(repo, NULL, opts, pathname, &files)) < 0 ||
22a2d3d5 271 (error = git_strmap_new(&seen)) < 0)
d58336dd 272 goto cleanup;
ee1f0b1a 273
ee1f0b1a
RB
274 git_vector_foreach(&files, i, file) {
275
276 git_attr_file__foreach_matching_rule(file, &path, j, rule) {
277
278 git_vector_foreach(&rule->assigns, k, assign) {
279 /* skip if higher priority assignment was already seen */
c2b67043 280 if (git_strmap_exists(seen, assign->name))
ee1f0b1a
RB
281 continue;
282
22a2d3d5 283 if ((error = git_strmap_set(seen, assign->name, assign)) < 0)
5dca2010 284 goto cleanup;
ee1f0b1a 285
c7b3e1b3
RB
286 error = callback(assign->name, assign->value, payload);
287 if (error) {
ac3d33df 288 git_error_set_after_callback(error);
ee1f0b1a 289 goto cleanup;
c7b3e1b3 290 }
ee1f0b1a
RB
291 }
292 }
293 }
294
295cleanup:
c2b67043 296 git_strmap_free(seen);
40ed4990 297 release_attr_files(&files);
d58336dd 298 git_attr_path__free(&path);
ee1f0b1a 299
ee1f0b1a
RB
300 return error;
301}
302
c25aa7cd 303static int preload_attr_source(
e3a2a04c 304 git_repository *repo,
9f779aac 305 git_attr_session *attr_session,
c25aa7cd 306 git_attr_file_source *source)
e3a2a04c
RB
307{
308 int error;
309 git_attr_file *preload = NULL;
310
c25aa7cd 311 if (!source)
e3a2a04c 312 return 0;
c25aa7cd
PP
313
314 error = git_attr_cache__get(&preload, repo, attr_session, source,
315 git_attr_file__parse_buffer, true);
316
317 if (!error)
e3a2a04c
RB
318 git_attr_file__free(preload);
319
320 return error;
321}
322
c25aa7cd
PP
323GIT_INLINE(int) preload_attr_file(
324 git_repository *repo,
325 git_attr_session *attr_session,
326 const char *base,
327 const char *filename)
328{
329 git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE };
330
331 if (!filename)
332 return 0;
333
334 source.base = base;
335 source.filename = filename;
336
337 return preload_attr_source(repo, attr_session, &source);
338}
339
d4b1b767
ET
340static int system_attr_file(
341 git_buf *out,
d4b1b767
ET
342 git_attr_session *attr_session)
343{
344 int error;
345
346 if (!attr_session) {
347 error = git_sysdir_find_system_file(out, GIT_ATTR_FILE_SYSTEM);
348
349 if (error == GIT_ENOTFOUND)
ac3d33df 350 git_error_clear();
d4b1b767
ET
351
352 return error;
353 }
354
355 if (!attr_session->init_sysdir) {
356 error = git_sysdir_find_system_file(&attr_session->sysdir, GIT_ATTR_FILE_SYSTEM);
357
358 if (error == GIT_ENOTFOUND)
ac3d33df 359 git_error_clear();
d4b1b767
ET
360 else if (error)
361 return error;
362
363 attr_session->init_sysdir = 1;
364 }
365
366 if (attr_session->sysdir.size == 0)
367 return GIT_ENOTFOUND;
368
369 /* We can safely provide a git_buf with no allocation (asize == 0) to
370 * a consumer. This allows them to treat this as a regular `git_buf`,
ac3d33df 371 * but their call to `git_buf_dispose` will not attempt to free it.
d4b1b767 372 */
d4cf1675
ET
373 git_buf_attach_notowned(
374 out, attr_session->sysdir.ptr, attr_session->sysdir.size);
d4b1b767
ET
375 return 0;
376}
377
22a2d3d5
UG
378static int attr_setup(
379 git_repository *repo,
380 git_attr_session *attr_session,
c25aa7cd 381 git_attr_options *opts)
e3a2a04c 382{
c25aa7cd
PP
383 git_buf system = GIT_BUF_INIT, info = GIT_BUF_INIT;
384 git_attr_file_source index_source = { GIT_ATTR_FILE_SOURCE_INDEX, NULL, GIT_ATTR_FILE, NULL };
385 git_attr_file_source head_source = { GIT_ATTR_FILE_SOURCE_HEAD, NULL, GIT_ATTR_FILE, NULL };
386 git_attr_file_source commit_source = { GIT_ATTR_FILE_SOURCE_COMMIT, NULL, GIT_ATTR_FILE, NULL };
22a2d3d5
UG
387 git_index *idx = NULL;
388 const char *workdir;
389 int error = 0;
e3a2a04c 390
d4b1b767 391 if (attr_session && attr_session->init_setup)
9f779aac
ET
392 return 0;
393
e3a2a04c
RB
394 if ((error = git_attr_cache__init(repo)) < 0)
395 return error;
396
22a2d3d5
UG
397 /*
398 * Preload attribute files that could contain macros so the
399 * definitions will be available for later file parsing.
e3a2a04c
RB
400 */
401
c25aa7cd
PP
402 if ((error = system_attr_file(&system, attr_session)) < 0 ||
403 (error = preload_attr_file(repo, attr_session, NULL, system.ptr)) < 0) {
22a2d3d5
UG
404 if (error != GIT_ENOTFOUND)
405 goto out;
c25aa7cd
PP
406
407 error = 0;
22a2d3d5 408 }
c5f3da96 409
c25aa7cd
PP
410 if ((error = preload_attr_file(repo, attr_session, NULL,
411 git_repository_attr_cache(repo)->cfg_attr_file)) < 0)
c5f3da96 412 goto out;
e3a2a04c 413
c25aa7cd
PP
414 if ((error = git_repository_item_path(&info, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 ||
415 (error = preload_attr_file(repo, attr_session, info.ptr, GIT_ATTR_FILE_INREPO)) < 0) {
22a2d3d5
UG
416 if (error != GIT_ENOTFOUND)
417 goto out;
c25aa7cd
PP
418
419 error = 0;
22a2d3d5 420 }
e3a2a04c 421
22a2d3d5 422 if ((workdir = git_repository_workdir(repo)) != NULL &&
c25aa7cd 423 (error = preload_attr_file(repo, attr_session, workdir, GIT_ATTR_FILE)) < 0)
22a2d3d5 424 goto out;
e3a2a04c
RB
425
426 if ((error = git_repository_index__weakptr(&idx, repo)) < 0 ||
c25aa7cd 427 (error = preload_attr_source(repo, attr_session, &index_source)) < 0)
22a2d3d5
UG
428 goto out;
429
c25aa7cd
PP
430 if ((opts && (opts->flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0) &&
431 (error = preload_attr_source(repo, attr_session, &head_source)) < 0)
c5f3da96 432 goto out;
e3a2a04c 433
c25aa7cd
PP
434 if ((opts && (opts->flags & GIT_ATTR_CHECK_INCLUDE_COMMIT) != 0)) {
435#ifndef GIT_DEPRECATE_HARD
436 if (opts->commit_id)
437 commit_source.commit_id = opts->commit_id;
438 else
439#endif
440 commit_source.commit_id = &opts->attr_commit_id;
441
442 if ((error = preload_attr_source(repo, attr_session, &commit_source)) < 0)
443 goto out;
444 }
445
9f779aac 446 if (attr_session)
d4b1b767 447 attr_session->init_setup = 1;
9f779aac 448
c5f3da96 449out:
c25aa7cd
PP
450 git_buf_dispose(&system);
451 git_buf_dispose(&info);
c5f3da96 452
e3a2a04c
RB
453 return error;
454}
455
73b51450
RB
456int git_attr_add_macro(
457 git_repository *repo,
458 const char *name,
459 const char *values)
460{
461 int error;
462 git_attr_rule *macro = NULL;
19fa2bc1 463 git_pool *pool;
73b51450 464
c25aa7cd
PP
465 GIT_ASSERT_ARG(repo);
466 GIT_ASSERT_ARG(name);
467
ac16bd0a 468 if ((error = git_attr_cache__init(repo)) < 0)
e3a2a04c 469 return error;
73b51450
RB
470
471 macro = git__calloc(1, sizeof(git_attr_rule));
ac3d33df 472 GIT_ERROR_CHECK_ALLOC(macro);
73b51450 473
19fa2bc1
RB
474 pool = &git_repository_attr_cache(repo)->pool;
475
476 macro->match.pattern = git_pool_strdup(pool, name);
ac3d33df 477 GIT_ERROR_CHECK_ALLOC(macro->match.pattern);
73b51450
RB
478
479 macro->match.length = strlen(macro->match.pattern);
480 macro->match.flags = GIT_ATTR_FNMATCH_MACRO;
481
19fa2bc1 482 error = git_attr_assignment__parse(repo, pool, &macro->assigns, &values);
73b51450 483
0d0fa7c3 484 if (!error)
73b51450
RB
485 error = git_attr_cache__insert_macro(repo, macro);
486
0d0fa7c3 487 if (error < 0)
73b51450 488 git_attr_rule__free(macro);
73b51450
RB
489
490 return error;
491}
492
0cfcff5d
RB
493typedef struct {
494 git_repository *repo;
9f779aac 495 git_attr_session *attr_session;
c25aa7cd 496 git_attr_options *opts;
f917481e
RB
497 const char *workdir;
498 git_index *index;
0cfcff5d
RB
499 git_vector *files;
500} attr_walk_up_info;
501
7d490872 502static int attr_decide_sources(
c25aa7cd
PP
503 uint32_t flags,
504 bool has_wd,
505 bool has_index,
506 git_attr_file_source_t *srcs)
f917481e
RB
507{
508 int count = 0;
509
510 switch (flags & 0x03) {
511 case GIT_ATTR_CHECK_FILE_THEN_INDEX:
512 if (has_wd)
c25aa7cd 513 srcs[count++] = GIT_ATTR_FILE_SOURCE_FILE;
f917481e 514 if (has_index)
c25aa7cd 515 srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX;
f917481e
RB
516 break;
517 case GIT_ATTR_CHECK_INDEX_THEN_FILE:
518 if (has_index)
c25aa7cd 519 srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX;
f917481e 520 if (has_wd)
c25aa7cd 521 srcs[count++] = GIT_ATTR_FILE_SOURCE_FILE;
f917481e
RB
522 break;
523 case GIT_ATTR_CHECK_INDEX_ONLY:
524 if (has_index)
c25aa7cd 525 srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX;
f917481e
RB
526 break;
527 }
528
22a2d3d5 529 if ((flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0)
c25aa7cd
PP
530 srcs[count++] = GIT_ATTR_FILE_SOURCE_HEAD;
531
532 if ((flags & GIT_ATTR_CHECK_INCLUDE_COMMIT) != 0)
533 srcs[count++] = GIT_ATTR_FILE_SOURCE_COMMIT;
22a2d3d5 534
f917481e
RB
535 return count;
536}
537
c25aa7cd 538static int push_attr_source(
7d490872 539 git_repository *repo,
9f779aac 540 git_attr_session *attr_session,
7d490872 541 git_vector *list,
c25aa7cd 542 git_attr_file_source *source,
22a2d3d5 543 bool allow_macros)
7d490872
RB
544{
545 int error = 0;
546 git_attr_file *file = NULL;
547
9f779aac 548 error = git_attr_cache__get(&file, repo, attr_session,
c25aa7cd
PP
549 source,
550 git_attr_file__parse_buffer,
551 allow_macros);
9f779aac 552
2e9d813b
RB
553 if (error < 0)
554 return error;
555
556 if (file != NULL) {
557 if ((error = git_vector_insert(list, file)) < 0)
558 git_attr_file__free(file);
559 }
7d490872
RB
560
561 return error;
562}
563
c25aa7cd
PP
564GIT_INLINE(int) push_attr_file(
565 git_repository *repo,
566 git_attr_session *attr_session,
567 git_vector *list,
568 const char *base,
569 const char *filename)
570{
571 git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE, base, filename };
572 return push_attr_source(repo, attr_session, list, &source, true);
573}
574
bbb988a5 575static int push_one_attr(void *ref, const char *path)
0cfcff5d
RB
576{
577 attr_walk_up_info *info = (attr_walk_up_info *)ref;
c25aa7cd 578 git_attr_file_source_t src[GIT_ATTR_FILE_NUM_SOURCES];
22a2d3d5
UG
579 int error = 0, n_src, i;
580 bool allow_macros;
f917481e 581
c25aa7cd
PP
582 n_src = attr_decide_sources(info->opts ? info->opts->flags : 0,
583 info->workdir != NULL,
584 info->index != NULL,
585 src);
586
22a2d3d5 587 allow_macros = info->workdir ? !strcmp(info->workdir, path) : false;
f917481e 588
c25aa7cd
PP
589 for (i = 0; !error && i < n_src; ++i) {
590 git_attr_file_source source = { src[i], path, GIT_ATTR_FILE };
591
592 if (src[i] == GIT_ATTR_FILE_SOURCE_COMMIT && info->opts) {
593#ifndef GIT_DEPRECATE_HARD
594 if (info->opts->commit_id)
595 source.commit_id = info->opts->commit_id;
596 else
597#endif
598 source.commit_id = &info->opts->attr_commit_id;
599 }
600
601 error = push_attr_source(info->repo, info->attr_session, info->files,
602 &source, allow_macros);
603 }
f917481e 604
25e0b157 605 return error;
0cfcff5d
RB
606}
607
40ed4990
RB
608static void release_attr_files(git_vector *files)
609{
610 size_t i;
611 git_attr_file *file;
612
613 git_vector_foreach(files, i, file) {
614 git_attr_file__free(file);
615 files->contents[i] = NULL;
616 }
617 git_vector_free(files);
618}
619
ee1f0b1a 620static int collect_attr_files(
f917481e 621 git_repository *repo,
9f779aac 622 git_attr_session *attr_session,
c25aa7cd 623 git_attr_options *opts,
f917481e
RB
624 const char *path,
625 git_vector *files)
ee1f0b1a 626{
52e9120c 627 int error = 0;
c5f3da96 628 git_buf dir = GIT_BUF_INIT, attrfile = GIT_BUF_INIT;
ee1f0b1a 629 const char *workdir = git_repository_workdir(repo);
dab89f9b 630 attr_walk_up_info info = { NULL };
ee1f0b1a 631
c25aa7cd
PP
632 GIT_ASSERT(!git_path_is_absolute(path));
633
634 if ((error = attr_setup(repo, attr_session, opts)) < 0)
2e9d813b 635 return error;
ee1f0b1a 636
cba285d3 637 /* Resolve path in a non-bare repo */
c25aa7cd
PP
638 if (workdir != NULL) {
639 if (!(error = git_repository_workdir_path(&dir, repo, path)))
640 error = git_path_find_dir(&dir);
641 }
642 else {
a2b4407d 643 error = git_path_dirname_r(&dir, path);
c25aa7cd
PP
644 }
645
ab43ad2f 646 if (error < 0)
ee1f0b1a
RB
647 goto cleanup;
648
ee1f0b1a
RB
649 /* in precendence order highest to lowest:
650 * - $GIT_DIR/info/attributes
651 * - path components with .gitattributes
652 * - config core.attributesfile
653 * - $GIT_PREFIX/etc/gitattributes
654 */
655
22a2d3d5 656 if ((error = git_repository_item_path(&attrfile, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 ||
c25aa7cd 657 (error = push_attr_file(repo, attr_session, files, attrfile.ptr, GIT_ATTR_FILE_INREPO)) < 0) {
22a2d3d5
UG
658 if (error != GIT_ENOTFOUND)
659 goto cleanup;
660 }
ee1f0b1a 661
9f779aac
ET
662 info.repo = repo;
663 info.attr_session = attr_session;
c25aa7cd 664 info.opts = opts;
f917481e
RB
665 info.workdir = workdir;
666 if (git_repository_index__weakptr(&info.index, repo) < 0)
ac3d33df 667 git_error_clear(); /* no error even if there is no index */
0cfcff5d 668 info.files = files;
f917481e 669
4e964117 670 if (!strcmp(dir.ptr, "."))
a2b4407d 671 error = push_one_attr(&info, "");
4e964117 672 else
a2b4407d 673 error = git_path_walk_up(&dir, workdir, push_one_attr, &info);
a2b4407d 674
ab43ad2f 675 if (error < 0)
ee1f0b1a
RB
676 goto cleanup;
677
95dfb031 678 if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) {
c25aa7cd 679 error = push_attr_file(repo, attr_session, files, NULL, git_repository_attr_cache(repo)->cfg_attr_file);
95dfb031
RB
680 if (error < 0)
681 goto cleanup;
ee1f0b1a
RB
682 }
683
c25aa7cd 684 if (!opts || (opts->flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {
fa89ff20 685 error = system_attr_file(&dir, attr_session);
d4b1b767 686
f917481e 687 if (!error)
c25aa7cd 688 error = push_attr_file(repo, attr_session, files, NULL, dir.ptr);
d4b1b767 689 else if (error == GIT_ENOTFOUND)
f917481e
RB
690 error = 0;
691 }
ee1f0b1a
RB
692
693 cleanup:
ab43ad2f 694 if (error < 0)
40ed4990 695 release_attr_files(files);
ac3d33df
JK
696 git_buf_dispose(&attrfile);
697 git_buf_dispose(&dir);
ee1f0b1a
RB
698
699 return error;
700}