2 #include "repository.h"
12 const char *git_attr__true
= "[internal]__TRUE__";
13 const char *git_attr__false
= "[internal]__FALSE__";
14 const char *git_attr__unset
= "[internal]__UNSET__";
16 git_attr_t
git_attr_value(const char *attr
)
18 if (attr
== NULL
|| attr
== git_attr__unset
)
19 return GIT_ATTR_UNSPECIFIED_T
;
21 if (attr
== git_attr__true
)
22 return GIT_ATTR_TRUE_T
;
24 if (attr
== git_attr__false
)
25 return GIT_ATTR_FALSE_T
;
27 return GIT_ATTR_VALUE_T
;
30 static int collect_attr_files(
32 git_attr_session
*attr_session
,
37 static void release_attr_files(git_vector
*files
);
48 git_vector files
= GIT_VECTOR_INIT
;
54 assert(value
&& repo
&& name
);
58 if (git_attr_path__init(&path
, pathname
, git_repository_workdir(repo
), GIT_DIR_FLAG_UNKNOWN
) < 0)
61 if ((error
= collect_attr_files(repo
, NULL
, flags
, pathname
, &files
)) < 0)
64 memset(&attr
, 0, sizeof(attr
));
66 attr
.name_hash
= git_attr_file__name_hash(name
);
68 git_vector_foreach(&files
, i
, file
) {
70 git_attr_file__foreach_matching_rule(file
, &path
, j
, rule
) {
73 if (!git_vector_bsearch(&pos
, &rule
->assigns
, &attr
)) {
74 *value
= ((git_attr_assignment
*)git_vector_get(
75 &rule
->assigns
, pos
))->value
;
82 release_attr_files(&files
);
83 git_attr_path__free(&path
);
91 git_attr_assignment
*found
;
94 int git_attr_get_many_with_session(
97 git_attr_session
*attr_session
,
105 git_vector files
= GIT_VECTOR_INIT
;
109 attr_get_many_info
*info
= NULL
;
110 size_t num_found
= 0;
115 assert(values
&& repo
&& names
);
117 if (git_attr_path__init(&path
, pathname
, git_repository_workdir(repo
), GIT_DIR_FLAG_UNKNOWN
) < 0)
120 if ((error
= collect_attr_files(repo
, attr_session
, flags
, pathname
, &files
)) < 0)
123 info
= git__calloc(num_attr
, sizeof(attr_get_many_info
));
124 GITERR_CHECK_ALLOC(info
);
126 git_vector_foreach(&files
, i
, file
) {
128 git_attr_file__foreach_matching_rule(file
, &path
, j
, rule
) {
130 for (k
= 0; k
< num_attr
; k
++) {
133 if (info
[k
].found
!= NULL
) /* already found assignment */
136 if (!info
[k
].name
.name
) {
137 info
[k
].name
.name
= names
[k
];
138 info
[k
].name
.name_hash
= git_attr_file__name_hash(names
[k
]);
141 if (!git_vector_bsearch(&pos
, &rule
->assigns
, &info
[k
].name
)) {
142 info
[k
].found
= (git_attr_assignment
*)
143 git_vector_get(&rule
->assigns
, pos
);
144 values
[k
] = info
[k
].found
->value
;
146 if (++num_found
== num_attr
)
153 for (k
= 0; k
< num_attr
; k
++) {
159 release_attr_files(&files
);
160 git_attr_path__free(&path
);
166 int git_attr_get_many(
168 git_repository
*repo
,
170 const char *pathname
,
174 return git_attr_get_many_with_session(
175 values
, repo
, NULL
, flags
, pathname
, num_attr
, names
);
178 int git_attr_foreach(
179 git_repository
*repo
,
181 const char *pathname
,
182 int (*callback
)(const char *name
, const char *value
, void *payload
),
187 git_vector files
= GIT_VECTOR_INIT
;
191 git_attr_assignment
*assign
;
192 git_strmap
*seen
= NULL
;
194 assert(repo
&& callback
);
196 if (git_attr_path__init(&path
, pathname
, git_repository_workdir(repo
), GIT_DIR_FLAG_UNKNOWN
) < 0)
199 if ((error
= collect_attr_files(repo
, NULL
, flags
, pathname
, &files
)) < 0 ||
200 (error
= git_strmap_alloc(&seen
)) < 0)
203 git_vector_foreach(&files
, i
, file
) {
205 git_attr_file__foreach_matching_rule(file
, &path
, j
, rule
) {
207 git_vector_foreach(&rule
->assigns
, k
, assign
) {
208 /* skip if higher priority assignment was already seen */
209 if (git_strmap_exists(seen
, assign
->name
))
212 git_strmap_insert(seen
, assign
->name
, assign
, error
);
216 error
= callback(assign
->name
, assign
->value
, payload
);
218 giterr_set_after_callback(error
);
226 git_strmap_free(seen
);
227 release_attr_files(&files
);
228 git_attr_path__free(&path
);
233 static int preload_attr_file(
234 git_repository
*repo
,
235 git_attr_session
*attr_session
,
236 git_attr_file_source source
,
241 git_attr_file
*preload
= NULL
;
245 if (!(error
= git_attr_cache__get(
246 &preload
, repo
, attr_session
, source
, base
, file
, git_attr_file__parse_buffer
)))
247 git_attr_file__free(preload
);
252 static int system_attr_file(
254 git_attr_session
*attr_session
)
259 error
= git_sysdir_find_system_file(out
, GIT_ATTR_FILE_SYSTEM
);
261 if (error
== GIT_ENOTFOUND
)
267 if (!attr_session
->init_sysdir
) {
268 error
= git_sysdir_find_system_file(&attr_session
->sysdir
, GIT_ATTR_FILE_SYSTEM
);
270 if (error
== GIT_ENOTFOUND
)
275 attr_session
->init_sysdir
= 1;
278 if (attr_session
->sysdir
.size
== 0)
279 return GIT_ENOTFOUND
;
281 /* We can safely provide a git_buf with no allocation (asize == 0) to
282 * a consumer. This allows them to treat this as a regular `git_buf`,
283 * but their call to `git_buf_free` will not attempt to free it.
285 git_buf_attach_notowned(
286 out
, attr_session
->sysdir
.ptr
, attr_session
->sysdir
.size
);
290 static int attr_setup(git_repository
*repo
, git_attr_session
*attr_session
)
293 const char *workdir
= git_repository_workdir(repo
);
294 git_index
*idx
= NULL
;
295 git_buf path
= GIT_BUF_INIT
;
297 if (attr_session
&& attr_session
->init_setup
)
300 if ((error
= git_attr_cache__init(repo
)) < 0)
303 /* preload attribute files that could contain macros so the
304 * definitions will be available for later file parsing
307 error
= system_attr_file(&path
, attr_session
);
310 error
= preload_attr_file(
311 repo
, attr_session
, GIT_ATTR_FILE__FROM_FILE
, NULL
, path
.ptr
);
313 if (error
!= GIT_ENOTFOUND
)
316 if ((error
= preload_attr_file(
317 repo
, attr_session
, GIT_ATTR_FILE__FROM_FILE
,
318 NULL
, git_repository_attr_cache(repo
)->cfg_attr_file
)) < 0)
321 if ((error
= git_repository_item_path(&path
,
322 repo
, GIT_REPOSITORY_ITEM_INFO
)) < 0)
325 if ((error
= preload_attr_file(
326 repo
, attr_session
, GIT_ATTR_FILE__FROM_FILE
,
327 path
.ptr
, GIT_ATTR_FILE_INREPO
)) < 0)
330 if (workdir
!= NULL
&&
331 (error
= preload_attr_file(
332 repo
, attr_session
, GIT_ATTR_FILE__FROM_FILE
, workdir
, GIT_ATTR_FILE
)) < 0)
335 if ((error
= git_repository_index__weakptr(&idx
, repo
)) < 0 ||
336 (error
= preload_attr_file(
337 repo
, attr_session
, GIT_ATTR_FILE__FROM_INDEX
, NULL
, GIT_ATTR_FILE
)) < 0)
341 attr_session
->init_setup
= 1;
349 int git_attr_add_macro(
350 git_repository
*repo
,
355 git_attr_rule
*macro
= NULL
;
358 if ((error
= git_attr_cache__init(repo
)) < 0)
361 macro
= git__calloc(1, sizeof(git_attr_rule
));
362 GITERR_CHECK_ALLOC(macro
);
364 pool
= &git_repository_attr_cache(repo
)->pool
;
366 macro
->match
.pattern
= git_pool_strdup(pool
, name
);
367 GITERR_CHECK_ALLOC(macro
->match
.pattern
);
369 macro
->match
.length
= strlen(macro
->match
.pattern
);
370 macro
->match
.flags
= GIT_ATTR_FNMATCH_MACRO
;
372 error
= git_attr_assignment__parse(repo
, pool
, ¯o
->assigns
, &values
);
375 error
= git_attr_cache__insert_macro(repo
, macro
);
378 git_attr_rule__free(macro
);
384 git_repository
*repo
;
385 git_attr_session
*attr_session
;
392 static int attr_decide_sources(
393 uint32_t flags
, bool has_wd
, bool has_index
, git_attr_file_source
*srcs
)
397 switch (flags
& 0x03) {
398 case GIT_ATTR_CHECK_FILE_THEN_INDEX
:
400 srcs
[count
++] = GIT_ATTR_FILE__FROM_FILE
;
402 srcs
[count
++] = GIT_ATTR_FILE__FROM_INDEX
;
404 case GIT_ATTR_CHECK_INDEX_THEN_FILE
:
406 srcs
[count
++] = GIT_ATTR_FILE__FROM_INDEX
;
408 srcs
[count
++] = GIT_ATTR_FILE__FROM_FILE
;
410 case GIT_ATTR_CHECK_INDEX_ONLY
:
412 srcs
[count
++] = GIT_ATTR_FILE__FROM_INDEX
;
419 static int push_attr_file(
420 git_repository
*repo
,
421 git_attr_session
*attr_session
,
423 git_attr_file_source source
,
425 const char *filename
)
428 git_attr_file
*file
= NULL
;
430 error
= git_attr_cache__get(&file
, repo
, attr_session
,
431 source
, base
, filename
, git_attr_file__parse_buffer
);
437 if ((error
= git_vector_insert(list
, file
)) < 0)
438 git_attr_file__free(file
);
444 static int push_one_attr(void *ref
, const char *path
)
446 int error
= 0, n_src
, i
;
447 attr_walk_up_info
*info
= (attr_walk_up_info
*)ref
;
448 git_attr_file_source src
[2];
450 n_src
= attr_decide_sources(
451 info
->flags
, info
->workdir
!= NULL
, info
->index
!= NULL
, src
);
453 for (i
= 0; !error
&& i
< n_src
; ++i
)
454 error
= push_attr_file(info
->repo
, info
->attr_session
,
455 info
->files
, src
[i
], path
, GIT_ATTR_FILE
);
460 static void release_attr_files(git_vector
*files
)
465 git_vector_foreach(files
, i
, file
) {
466 git_attr_file__free(file
);
467 files
->contents
[i
] = NULL
;
469 git_vector_free(files
);
472 static int collect_attr_files(
473 git_repository
*repo
,
474 git_attr_session
*attr_session
,
480 git_buf dir
= GIT_BUF_INIT
, attrfile
= GIT_BUF_INIT
;
481 const char *workdir
= git_repository_workdir(repo
);
482 attr_walk_up_info info
= { NULL
};
484 if ((error
= attr_setup(repo
, attr_session
)) < 0)
487 /* Resolve path in a non-bare repo */
489 error
= git_path_find_dir(&dir
, path
, workdir
);
491 error
= git_path_dirname_r(&dir
, path
);
495 /* in precendence order highest to lowest:
496 * - $GIT_DIR/info/attributes
497 * - path components with .gitattributes
498 * - config core.attributesfile
499 * - $GIT_PREFIX/etc/gitattributes
502 error
= git_repository_item_path(&attrfile
, repo
, GIT_REPOSITORY_ITEM_INFO
);
506 error
= push_attr_file(
507 repo
, attr_session
, files
, GIT_ATTR_FILE__FROM_FILE
,
508 attrfile
.ptr
, GIT_ATTR_FILE_INREPO
);
513 info
.attr_session
= attr_session
;
515 info
.workdir
= workdir
;
516 if (git_repository_index__weakptr(&info
.index
, repo
) < 0)
517 giterr_clear(); /* no error even if there is no index */
520 if (!strcmp(dir
.ptr
, "."))
521 error
= push_one_attr(&info
, "");
523 error
= git_path_walk_up(&dir
, workdir
, push_one_attr
, &info
);
528 if (git_repository_attr_cache(repo
)->cfg_attr_file
!= NULL
) {
529 error
= push_attr_file(
530 repo
, attr_session
, files
, GIT_ATTR_FILE__FROM_FILE
,
531 NULL
, git_repository_attr_cache(repo
)->cfg_attr_file
);
536 if ((flags
& GIT_ATTR_CHECK_NO_SYSTEM
) == 0) {
537 error
= system_attr_file(&dir
, attr_session
);
540 error
= push_attr_file(
541 repo
, attr_session
, files
, GIT_ATTR_FILE__FROM_FILE
,
543 else if (error
== GIT_ENOTFOUND
)
549 release_attr_files(files
);
550 git_buf_free(&attrfile
);