]>
Commit | Line | Data |
---|---|---|
157cef10 | 1 | #include "common.h" |
73b51450 | 2 | #include "repository.h" |
83634d38 | 3 | #include "sysdir.h" |
ee1f0b1a | 4 | #include "config.h" |
7d490872 | 5 | #include "attr_file.h" |
5540d947 | 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 | 35 | |
40ed4990 | 36 | static void release_attr_files(git_vector *files); |
ee1f0b1a RB |
37 | |
38 | int git_attr_get( | |
29e948de | 39 | const char **value, |
0cb16fe9 | 40 | git_repository *repo, |
f917481e RB |
41 | uint32_t flags, |
42 | const char *pathname, | |
29e948de | 43 | const char *name) |
ee1f0b1a RB |
44 | { |
45 | int error; | |
46 | git_attr_path path; | |
47 | git_vector files = GIT_VECTOR_INIT; | |
b8457baa | 48 | size_t i, j; |
ee1f0b1a RB |
49 | git_attr_file *file; |
50 | git_attr_name attr; | |
51 | git_attr_rule *rule; | |
52 | ||
3cf11eef RB |
53 | assert(value && repo && name); |
54 | ||
ee1f0b1a RB |
55 | *value = NULL; |
56 | ||
d58336dd RB |
57 | if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0) |
58 | return -1; | |
59 | ||
f917481e | 60 | if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0) |
d58336dd | 61 | goto cleanup; |
ee1f0b1a | 62 | |
2e9d813b | 63 | memset(&attr, 0, sizeof(attr)); |
ee1f0b1a RB |
64 | attr.name = name; |
65 | attr.name_hash = git_attr_file__name_hash(name); | |
66 | ||
67 | git_vector_foreach(&files, i, file) { | |
68 | ||
69 | git_attr_file__foreach_matching_rule(file, &path, j, rule) { | |
11d9f6b3 PK |
70 | size_t pos; |
71 | ||
72 | if (!git_vector_bsearch(&pos, &rule->assigns, &attr)) { | |
ee1f0b1a RB |
73 | *value = ((git_attr_assignment *)git_vector_get( |
74 | &rule->assigns, pos))->value; | |
d58336dd | 75 | goto cleanup; |
ee1f0b1a RB |
76 | } |
77 | } | |
78 | } | |
79 | ||
d58336dd | 80 | cleanup: |
40ed4990 | 81 | release_attr_files(&files); |
d58336dd | 82 | git_attr_path__free(&path); |
ee1f0b1a RB |
83 | |
84 | return error; | |
85 | } | |
86 | ||
87 | ||
88 | typedef struct { | |
89 | git_attr_name name; | |
90 | git_attr_assignment *found; | |
91 | } attr_get_many_info; | |
92 | ||
93 | int git_attr_get_many( | |
29e948de | 94 | const char **values, |
0cb16fe9 | 95 | git_repository *repo, |
f917481e RB |
96 | uint32_t flags, |
97 | const char *pathname, | |
0cb16fe9 | 98 | size_t num_attr, |
29e948de | 99 | const char **names) |
ee1f0b1a RB |
100 | { |
101 | int error; | |
102 | git_attr_path path; | |
103 | git_vector files = GIT_VECTOR_INIT; | |
b8457baa | 104 | size_t i, j, k; |
ee1f0b1a RB |
105 | git_attr_file *file; |
106 | git_attr_rule *rule; | |
107 | attr_get_many_info *info = NULL; | |
108 | size_t num_found = 0; | |
109 | ||
3cf11eef RB |
110 | if (!num_attr) |
111 | return 0; | |
112 | ||
113 | assert(values && repo && names); | |
114 | ||
d58336dd RB |
115 | if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0) |
116 | return -1; | |
117 | ||
f917481e | 118 | if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0) |
d58336dd | 119 | goto cleanup; |
ee1f0b1a | 120 | |
0d0fa7c3 RB |
121 | info = git__calloc(num_attr, sizeof(attr_get_many_info)); |
122 | GITERR_CHECK_ALLOC(info); | |
ee1f0b1a RB |
123 | |
124 | git_vector_foreach(&files, i, file) { | |
125 | ||
126 | git_attr_file__foreach_matching_rule(file, &path, j, rule) { | |
127 | ||
128 | for (k = 0; k < num_attr; k++) { | |
11d9f6b3 | 129 | size_t pos; |
ee1f0b1a RB |
130 | |
131 | if (info[k].found != NULL) /* already found assignment */ | |
132 | continue; | |
133 | ||
134 | if (!info[k].name.name) { | |
135 | info[k].name.name = names[k]; | |
136 | info[k].name.name_hash = git_attr_file__name_hash(names[k]); | |
137 | } | |
138 | ||
11d9f6b3 | 139 | if (!git_vector_bsearch(&pos, &rule->assigns, &info[k].name)) { |
ee1f0b1a RB |
140 | info[k].found = (git_attr_assignment *) |
141 | git_vector_get(&rule->assigns, pos); | |
142 | values[k] = info[k].found->value; | |
143 | ||
144 | if (++num_found == num_attr) | |
145 | goto cleanup; | |
146 | } | |
147 | } | |
148 | } | |
149 | } | |
150 | ||
974774c7 RB |
151 | for (k = 0; k < num_attr; k++) { |
152 | if (!info[k].found) | |
153 | values[k] = NULL; | |
154 | } | |
155 | ||
ee1f0b1a | 156 | cleanup: |
40ed4990 | 157 | release_attr_files(&files); |
d58336dd | 158 | git_attr_path__free(&path); |
ee1f0b1a RB |
159 | git__free(info); |
160 | ||
161 | return error; | |
162 | } | |
163 | ||
164 | ||
165 | int git_attr_foreach( | |
0cb16fe9 | 166 | git_repository *repo, |
f917481e RB |
167 | uint32_t flags, |
168 | const char *pathname, | |
ee1f0b1a RB |
169 | int (*callback)(const char *name, const char *value, void *payload), |
170 | void *payload) | |
171 | { | |
172 | int error; | |
173 | git_attr_path path; | |
174 | git_vector files = GIT_VECTOR_INIT; | |
b8457baa | 175 | size_t i, j, k; |
ee1f0b1a RB |
176 | git_attr_file *file; |
177 | git_attr_rule *rule; | |
178 | git_attr_assignment *assign; | |
c2b67043 | 179 | git_strmap *seen = NULL; |
ee1f0b1a | 180 | |
3cf11eef RB |
181 | assert(repo && callback); |
182 | ||
d58336dd RB |
183 | if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0) |
184 | return -1; | |
185 | ||
40ed4990 RB |
186 | if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0 || |
187 | (error = git_strmap_alloc(&seen)) < 0) | |
d58336dd | 188 | goto cleanup; |
ee1f0b1a | 189 | |
ee1f0b1a RB |
190 | git_vector_foreach(&files, i, file) { |
191 | ||
192 | git_attr_file__foreach_matching_rule(file, &path, j, rule) { | |
193 | ||
194 | git_vector_foreach(&rule->assigns, k, assign) { | |
195 | /* skip if higher priority assignment was already seen */ | |
c2b67043 | 196 | if (git_strmap_exists(seen, assign->name)) |
ee1f0b1a RB |
197 | continue; |
198 | ||
c2b67043 | 199 | git_strmap_insert(seen, assign->name, assign, error); |
5dca2010 RB |
200 | if (error < 0) |
201 | goto cleanup; | |
ee1f0b1a | 202 | |
c7b3e1b3 RB |
203 | error = callback(assign->name, assign->value, payload); |
204 | if (error) { | |
26c1cb91 | 205 | giterr_set_after_callback(error); |
ee1f0b1a | 206 | goto cleanup; |
c7b3e1b3 | 207 | } |
ee1f0b1a RB |
208 | } |
209 | } | |
210 | } | |
211 | ||
212 | cleanup: | |
c2b67043 | 213 | git_strmap_free(seen); |
40ed4990 | 214 | release_attr_files(&files); |
d58336dd | 215 | git_attr_path__free(&path); |
ee1f0b1a | 216 | |
ee1f0b1a RB |
217 | return error; |
218 | } | |
219 | ||
e3a2a04c RB |
220 | static int preload_attr_file( |
221 | git_repository *repo, | |
222 | git_attr_file_source source, | |
223 | const char *base, | |
224 | const char *file) | |
225 | { | |
226 | int error; | |
227 | git_attr_file *preload = NULL; | |
228 | ||
229 | if (!file) | |
230 | return 0; | |
231 | if (!(error = git_attr_cache__get( | |
232 | &preload, repo, source, base, file, git_attr_file__parse_buffer))) | |
233 | git_attr_file__free(preload); | |
234 | ||
235 | return error; | |
236 | } | |
237 | ||
238 | static int attr_setup(git_repository *repo) | |
239 | { | |
240 | int error = 0; | |
241 | const char *workdir = git_repository_workdir(repo); | |
242 | git_index *idx = NULL; | |
243 | git_buf sys = GIT_BUF_INIT; | |
244 | ||
245 | if ((error = git_attr_cache__init(repo)) < 0) | |
246 | return error; | |
247 | ||
248 | /* preload attribute files that could contain macros so the | |
249 | * definitions will be available for later file parsing | |
250 | */ | |
251 | ||
252 | if (!(error = git_sysdir_find_system_file(&sys, GIT_ATTR_FILE_SYSTEM))) { | |
253 | error = preload_attr_file( | |
254 | repo, GIT_ATTR_FILE__FROM_FILE, NULL, sys.ptr); | |
255 | git_buf_free(&sys); | |
256 | } | |
257 | if (error < 0) { | |
258 | if (error == GIT_ENOTFOUND) { | |
259 | giterr_clear(); | |
260 | error = 0; | |
261 | } else | |
262 | return error; | |
263 | } | |
264 | ||
265 | if ((error = preload_attr_file( | |
266 | repo, GIT_ATTR_FILE__FROM_FILE, | |
267 | NULL, git_repository_attr_cache(repo)->cfg_attr_file)) < 0) | |
268 | return error; | |
269 | ||
270 | if ((error = preload_attr_file( | |
271 | repo, GIT_ATTR_FILE__FROM_FILE, | |
272 | git_repository_path(repo), GIT_ATTR_FILE_INREPO)) < 0) | |
273 | return error; | |
274 | ||
275 | if (workdir != NULL && | |
276 | (error = preload_attr_file( | |
277 | repo, GIT_ATTR_FILE__FROM_FILE, workdir, GIT_ATTR_FILE)) < 0) | |
278 | return error; | |
279 | ||
280 | if ((error = git_repository_index__weakptr(&idx, repo)) < 0 || | |
281 | (error = preload_attr_file( | |
282 | repo, GIT_ATTR_FILE__FROM_INDEX, NULL, GIT_ATTR_FILE)) < 0) | |
283 | return error; | |
284 | ||
285 | return error; | |
286 | } | |
287 | ||
73b51450 RB |
288 | int git_attr_add_macro( |
289 | git_repository *repo, | |
290 | const char *name, | |
291 | const char *values) | |
292 | { | |
293 | int error; | |
294 | git_attr_rule *macro = NULL; | |
19fa2bc1 | 295 | git_pool *pool; |
73b51450 | 296 | |
ac16bd0a | 297 | if ((error = git_attr_cache__init(repo)) < 0) |
e3a2a04c | 298 | return error; |
73b51450 RB |
299 | |
300 | macro = git__calloc(1, sizeof(git_attr_rule)); | |
0d0fa7c3 | 301 | GITERR_CHECK_ALLOC(macro); |
73b51450 | 302 | |
19fa2bc1 RB |
303 | pool = &git_repository_attr_cache(repo)->pool; |
304 | ||
305 | macro->match.pattern = git_pool_strdup(pool, name); | |
0d0fa7c3 | 306 | GITERR_CHECK_ALLOC(macro->match.pattern); |
73b51450 RB |
307 | |
308 | macro->match.length = strlen(macro->match.pattern); | |
309 | macro->match.flags = GIT_ATTR_FNMATCH_MACRO; | |
310 | ||
19fa2bc1 | 311 | error = git_attr_assignment__parse(repo, pool, ¯o->assigns, &values); |
73b51450 | 312 | |
0d0fa7c3 | 313 | if (!error) |
73b51450 RB |
314 | error = git_attr_cache__insert_macro(repo, macro); |
315 | ||
0d0fa7c3 | 316 | if (error < 0) |
73b51450 | 317 | git_attr_rule__free(macro); |
73b51450 RB |
318 | |
319 | return error; | |
320 | } | |
321 | ||
0cfcff5d RB |
322 | typedef struct { |
323 | git_repository *repo; | |
f917481e RB |
324 | uint32_t flags; |
325 | const char *workdir; | |
326 | git_index *index; | |
0cfcff5d RB |
327 | git_vector *files; |
328 | } attr_walk_up_info; | |
329 | ||
7d490872 | 330 | static int attr_decide_sources( |
823c0e9c | 331 | uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs) |
f917481e RB |
332 | { |
333 | int count = 0; | |
334 | ||
335 | switch (flags & 0x03) { | |
336 | case GIT_ATTR_CHECK_FILE_THEN_INDEX: | |
337 | if (has_wd) | |
823c0e9c | 338 | srcs[count++] = GIT_ATTR_FILE__FROM_FILE; |
f917481e | 339 | if (has_index) |
823c0e9c | 340 | srcs[count++] = GIT_ATTR_FILE__FROM_INDEX; |
f917481e RB |
341 | break; |
342 | case GIT_ATTR_CHECK_INDEX_THEN_FILE: | |
343 | if (has_index) | |
823c0e9c | 344 | srcs[count++] = GIT_ATTR_FILE__FROM_INDEX; |
f917481e | 345 | if (has_wd) |
823c0e9c | 346 | srcs[count++] = GIT_ATTR_FILE__FROM_FILE; |
f917481e RB |
347 | break; |
348 | case GIT_ATTR_CHECK_INDEX_ONLY: | |
349 | if (has_index) | |
823c0e9c | 350 | srcs[count++] = GIT_ATTR_FILE__FROM_INDEX; |
f917481e RB |
351 | break; |
352 | } | |
353 | ||
354 | return count; | |
355 | } | |
356 | ||
7d490872 RB |
357 | static int push_attr_file( |
358 | git_repository *repo, | |
359 | git_vector *list, | |
823c0e9c | 360 | git_attr_file_source source, |
7d490872 RB |
361 | const char *base, |
362 | const char *filename) | |
363 | { | |
364 | int error = 0; | |
365 | git_attr_file *file = NULL; | |
366 | ||
2e9d813b | 367 | error = git_attr_cache__get( |
823c0e9c | 368 | &file, repo, source, base, filename, git_attr_file__parse_buffer); |
2e9d813b RB |
369 | if (error < 0) |
370 | return error; | |
371 | ||
372 | if (file != NULL) { | |
373 | if ((error = git_vector_insert(list, file)) < 0) | |
374 | git_attr_file__free(file); | |
375 | } | |
7d490872 RB |
376 | |
377 | return error; | |
378 | } | |
379 | ||
bbb988a5 | 380 | static int push_one_attr(void *ref, const char *path) |
0cfcff5d | 381 | { |
f917481e | 382 | int error = 0, n_src, i; |
0cfcff5d | 383 | attr_walk_up_info *info = (attr_walk_up_info *)ref; |
823c0e9c | 384 | git_attr_file_source src[2]; |
f917481e | 385 | |
7d490872 | 386 | n_src = attr_decide_sources( |
f917481e RB |
387 | info->flags, info->workdir != NULL, info->index != NULL, src); |
388 | ||
389 | for (i = 0; !error && i < n_src; ++i) | |
7d490872 | 390 | error = push_attr_file( |
bbb988a5 | 391 | info->repo, info->files, src[i], path, GIT_ATTR_FILE); |
f917481e | 392 | |
25e0b157 | 393 | return error; |
0cfcff5d RB |
394 | } |
395 | ||
40ed4990 RB |
396 | static void release_attr_files(git_vector *files) |
397 | { | |
398 | size_t i; | |
399 | git_attr_file *file; | |
400 | ||
401 | git_vector_foreach(files, i, file) { | |
402 | git_attr_file__free(file); | |
403 | files->contents[i] = NULL; | |
404 | } | |
405 | git_vector_free(files); | |
406 | } | |
407 | ||
ee1f0b1a | 408 | static int collect_attr_files( |
f917481e RB |
409 | git_repository *repo, |
410 | uint32_t flags, | |
411 | const char *path, | |
412 | git_vector *files) | |
ee1f0b1a | 413 | { |
52e9120c | 414 | int error = 0; |
ee1f0b1a | 415 | git_buf dir = GIT_BUF_INIT; |
ee1f0b1a | 416 | const char *workdir = git_repository_workdir(repo); |
dab89f9b | 417 | attr_walk_up_info info = { NULL }; |
ee1f0b1a | 418 | |
e3a2a04c | 419 | if ((error = attr_setup(repo)) < 0) |
2e9d813b | 420 | return error; |
ee1f0b1a | 421 | |
cba285d3 RB |
422 | /* Resolve path in a non-bare repo */ |
423 | if (workdir != NULL) | |
f917481e | 424 | error = git_path_find_dir(&dir, path, workdir); |
a2b4407d T |
425 | else |
426 | error = git_path_dirname_r(&dir, path); | |
ab43ad2f | 427 | if (error < 0) |
ee1f0b1a RB |
428 | goto cleanup; |
429 | ||
ee1f0b1a RB |
430 | /* in precendence order highest to lowest: |
431 | * - $GIT_DIR/info/attributes | |
432 | * - path components with .gitattributes | |
433 | * - config core.attributesfile | |
434 | * - $GIT_PREFIX/etc/gitattributes | |
435 | */ | |
436 | ||
f917481e | 437 | error = push_attr_file( |
823c0e9c | 438 | repo, files, GIT_ATTR_FILE__FROM_FILE, |
7d490872 | 439 | git_repository_path(repo), GIT_ATTR_FILE_INREPO); |
ab43ad2f | 440 | if (error < 0) |
ee1f0b1a RB |
441 | goto cleanup; |
442 | ||
f917481e RB |
443 | info.repo = repo; |
444 | info.flags = flags; | |
445 | info.workdir = workdir; | |
446 | if (git_repository_index__weakptr(&info.index, repo) < 0) | |
447 | giterr_clear(); /* no error even if there is no index */ | |
0cfcff5d | 448 | info.files = files; |
f917481e | 449 | |
a2b4407d T |
450 | if (!strcmp(dir.ptr, ".")) { |
451 | error = push_one_attr(&info, ""); | |
452 | } else { | |
453 | error = git_path_walk_up(&dir, workdir, push_one_attr, &info); | |
454 | } | |
455 | ||
ab43ad2f | 456 | if (error < 0) |
ee1f0b1a RB |
457 | goto cleanup; |
458 | ||
95dfb031 | 459 | if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) { |
f917481e | 460 | error = push_attr_file( |
823c0e9c | 461 | repo, files, GIT_ATTR_FILE__FROM_FILE, |
7d490872 | 462 | NULL, git_repository_attr_cache(repo)->cfg_attr_file); |
95dfb031 RB |
463 | if (error < 0) |
464 | goto cleanup; | |
ee1f0b1a RB |
465 | } |
466 | ||
f917481e | 467 | if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) { |
83634d38 | 468 | error = git_sysdir_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM); |
f917481e | 469 | if (!error) |
7d490872 | 470 | error = push_attr_file( |
823c0e9c | 471 | repo, files, GIT_ATTR_FILE__FROM_FILE, NULL, dir.ptr); |
6ac724af RB |
472 | else if (error == GIT_ENOTFOUND) { |
473 | giterr_clear(); | |
f917481e | 474 | error = 0; |
6ac724af | 475 | } |
f917481e | 476 | } |
ee1f0b1a RB |
477 | |
478 | cleanup: | |
ab43ad2f | 479 | if (error < 0) |
40ed4990 | 480 | release_attr_files(files); |
ee1f0b1a RB |
481 | git_buf_free(&dir); |
482 | ||
483 | return error; | |
484 | } |