Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Handle git attributes. See gitattributes(5) for a description of |
3 | | * the file syntax, and attr.h for a description of the API. |
4 | | * |
5 | | * One basic design decision here is that we are not going to support |
6 | | * an insanely large number of attributes. |
7 | | */ |
8 | | |
9 | | #include "git-compat-util.h" |
10 | | #include "config.h" |
11 | | #include "environment.h" |
12 | | #include "exec-cmd.h" |
13 | | #include "attr.h" |
14 | | #include "dir.h" |
15 | | #include "gettext.h" |
16 | | #include "path.h" |
17 | | #include "utf8.h" |
18 | | #include "quote.h" |
19 | | #include "read-cache-ll.h" |
20 | | #include "revision.h" |
21 | | #include "object-store-ll.h" |
22 | | #include "setup.h" |
23 | | #include "thread-utils.h" |
24 | | #include "tree-walk.h" |
25 | | #include "object-name.h" |
26 | | |
27 | | const char *git_attr_tree; |
28 | | |
29 | | const char git_attr__true[] = "(builtin)true"; |
30 | | const char git_attr__false[] = "\0(builtin)false"; |
31 | | static const char git_attr__unknown[] = "(builtin)unknown"; |
32 | 0 | #define ATTR__TRUE git_attr__true |
33 | 6 | #define ATTR__FALSE git_attr__false |
34 | 219k | #define ATTR__UNSET NULL |
35 | 675k | #define ATTR__UNKNOWN git_attr__unknown |
36 | | |
37 | | struct git_attr { |
38 | | unsigned int attr_nr; /* unique attribute number */ |
39 | | char name[FLEX_ARRAY]; /* attribute name */ |
40 | | }; |
41 | | |
42 | | const char *git_attr_name(const struct git_attr *attr) |
43 | 0 | { |
44 | 0 | return attr->name; |
45 | 0 | } |
46 | | |
47 | | struct attr_hashmap { |
48 | | struct hashmap map; |
49 | | pthread_mutex_t mutex; |
50 | | }; |
51 | | |
52 | | static inline void hashmap_lock(struct attr_hashmap *map) |
53 | 50.7k | { |
54 | 50.7k | pthread_mutex_lock(&map->mutex); |
55 | 50.7k | } |
56 | | |
57 | | static inline void hashmap_unlock(struct attr_hashmap *map) |
58 | 50.7k | { |
59 | 50.7k | pthread_mutex_unlock(&map->mutex); |
60 | 50.7k | } |
61 | | |
62 | | /* The container for objects stored in "struct attr_hashmap" */ |
63 | | struct attr_hash_entry { |
64 | | struct hashmap_entry ent; |
65 | | const char *key; /* the key; memory should be owned by value */ |
66 | | size_t keylen; /* length of the key */ |
67 | | void *value; /* the stored value */ |
68 | | }; |
69 | | |
70 | | /* attr_hashmap comparison function */ |
71 | | static int attr_hash_entry_cmp(const void *cmp_data UNUSED, |
72 | | const struct hashmap_entry *eptr, |
73 | | const struct hashmap_entry *entry_or_key, |
74 | | const void *keydata UNUSED) |
75 | 6 | { |
76 | 6 | const struct attr_hash_entry *a, *b; |
77 | | |
78 | 6 | a = container_of(eptr, const struct attr_hash_entry, ent); |
79 | 6 | b = container_of(entry_or_key, const struct attr_hash_entry, ent); |
80 | 6 | return (a->keylen != b->keylen) || strncmp(a->key, b->key, a->keylen); |
81 | 6 | } |
82 | | |
83 | | /* |
84 | | * The global dictionary of all interned attributes. This |
85 | | * is a singleton object which is shared between threads. |
86 | | * Access to this dictionary must be surrounded with a mutex. |
87 | | */ |
88 | | static struct attr_hashmap g_attr_hashmap = { |
89 | | .map = HASHMAP_INIT(attr_hash_entry_cmp, NULL), |
90 | | }; |
91 | | |
92 | | /* |
93 | | * Retrieve the 'value' stored in a hashmap given the provided 'key'. |
94 | | * If there is no matching entry, return NULL. |
95 | | */ |
96 | | static void *attr_hashmap_get(struct attr_hashmap *map, |
97 | | const char *key, size_t keylen) |
98 | 15 | { |
99 | 15 | struct attr_hash_entry k; |
100 | 15 | struct attr_hash_entry *e; |
101 | | |
102 | 15 | hashmap_entry_init(&k.ent, memhash(key, keylen)); |
103 | 15 | k.key = key; |
104 | 15 | k.keylen = keylen; |
105 | 15 | e = hashmap_get_entry(&map->map, &k, ent, NULL); |
106 | | |
107 | 15 | return e ? e->value : NULL; |
108 | 15 | } |
109 | | |
110 | | /* Add 'value' to a hashmap based on the provided 'key'. */ |
111 | | static void attr_hashmap_add(struct attr_hashmap *map, |
112 | | const char *key, size_t keylen, |
113 | | void *value) |
114 | 9 | { |
115 | 9 | struct attr_hash_entry *e; |
116 | | |
117 | 9 | e = xmalloc(sizeof(struct attr_hash_entry)); |
118 | 9 | hashmap_entry_init(&e->ent, memhash(key, keylen)); |
119 | 9 | e->key = key; |
120 | 9 | e->keylen = keylen; |
121 | 9 | e->value = value; |
122 | | |
123 | 9 | hashmap_add(&map->map, &e->ent); |
124 | 9 | } |
125 | | |
126 | | struct all_attrs_item { |
127 | | const struct git_attr *attr; |
128 | | const char *value; |
129 | | /* |
130 | | * If 'macro' is non-NULL, indicates that 'attr' is a macro based on |
131 | | * the current attribute stack and contains a pointer to the match_attr |
132 | | * definition of the macro |
133 | | */ |
134 | | const struct match_attr *macro; |
135 | | }; |
136 | | |
137 | | /* |
138 | | * Reallocate and reinitialize the array of all attributes (which is used in |
139 | | * the attribute collection process) in 'check' based on the global dictionary |
140 | | * of attributes. |
141 | | */ |
142 | | static void all_attrs_init(struct attr_hashmap *map, struct attr_check *check) |
143 | 50.6k | { |
144 | 50.6k | int i; |
145 | 50.6k | unsigned int size; |
146 | | |
147 | 50.6k | hashmap_lock(map); |
148 | | |
149 | 50.6k | size = hashmap_get_size(&map->map); |
150 | 50.6k | if (size < check->all_attrs_nr) |
151 | 0 | BUG("interned attributes shouldn't be deleted"); |
152 | | |
153 | | /* |
154 | | * If the number of attributes in the global dictionary has increased |
155 | | * (or this attr_check instance doesn't have an initialized all_attrs |
156 | | * field), reallocate the provided attr_check instance's all_attrs |
157 | | * field and fill each entry with its corresponding git_attr. |
158 | | */ |
159 | 50.6k | if (size != check->all_attrs_nr) { |
160 | 2 | struct attr_hash_entry *e; |
161 | 2 | struct hashmap_iter iter; |
162 | | |
163 | 2 | REALLOC_ARRAY(check->all_attrs, size); |
164 | 2 | check->all_attrs_nr = size; |
165 | | |
166 | 2 | hashmap_for_each_entry(&map->map, &iter, e, |
167 | 18 | ent /* member name */) { |
168 | 18 | const struct git_attr *a = e->value; |
169 | 18 | check->all_attrs[a->attr_nr].attr = a; |
170 | 18 | } |
171 | 2 | } |
172 | | |
173 | 50.6k | hashmap_unlock(map); |
174 | | |
175 | | /* |
176 | | * Re-initialize every entry in check->all_attrs. |
177 | | * This re-initialization can live outside of the locked region since |
178 | | * the attribute dictionary is no longer being accessed. |
179 | | */ |
180 | 506k | for (i = 0; i < check->all_attrs_nr; i++) { |
181 | 456k | check->all_attrs[i].value = ATTR__UNKNOWN; |
182 | 456k | check->all_attrs[i].macro = NULL; |
183 | 456k | } |
184 | 50.6k | } |
185 | | |
186 | | static int attr_name_valid(const char *name, size_t namelen) |
187 | 23 | { |
188 | | /* |
189 | | * Attribute name cannot begin with '-' and must consist of |
190 | | * characters from [-A-Za-z0-9_.]. |
191 | | */ |
192 | 23 | if (namelen <= 0 || *name == '-') |
193 | 0 | return 0; |
194 | 146 | while (namelen--) { |
195 | 123 | char ch = *name++; |
196 | 123 | if (! (ch == '-' || ch == '.' || ch == '_' || |
197 | 123 | ('0' <= ch && ch <= '9') || |
198 | 123 | ('a' <= ch && ch <= 'z') || |
199 | 123 | ('A' <= ch && ch <= 'Z')) ) |
200 | 0 | return 0; |
201 | 123 | } |
202 | 23 | return 1; |
203 | 23 | } |
204 | | |
205 | | static void report_invalid_attr(const char *name, size_t len, |
206 | | const char *src, int lineno) |
207 | 0 | { |
208 | 0 | struct strbuf err = STRBUF_INIT; |
209 | 0 | strbuf_addf(&err, _("%.*s is not a valid attribute name"), |
210 | 0 | (int) len, name); |
211 | 0 | fprintf(stderr, "%s: %s:%d\n", err.buf, src, lineno); |
212 | 0 | strbuf_release(&err); |
213 | 0 | } |
214 | | |
215 | | /* |
216 | | * Given a 'name', lookup and return the corresponding attribute in the global |
217 | | * dictionary. If no entry is found, create a new attribute and store it in |
218 | | * the dictionary. |
219 | | */ |
220 | | static const struct git_attr *git_attr_internal(const char *name, size_t namelen) |
221 | 15 | { |
222 | 15 | struct git_attr *a; |
223 | | |
224 | 15 | if (!attr_name_valid(name, namelen)) |
225 | 0 | return NULL; |
226 | | |
227 | 15 | hashmap_lock(&g_attr_hashmap); |
228 | | |
229 | 15 | a = attr_hashmap_get(&g_attr_hashmap, name, namelen); |
230 | | |
231 | 15 | if (!a) { |
232 | 9 | FLEX_ALLOC_MEM(a, name, name, namelen); |
233 | 9 | a->attr_nr = hashmap_get_size(&g_attr_hashmap.map); |
234 | | |
235 | 9 | attr_hashmap_add(&g_attr_hashmap, a->name, namelen, a); |
236 | 9 | if (a->attr_nr != hashmap_get_size(&g_attr_hashmap.map) - 1) |
237 | 0 | die(_("unable to add additional attribute")); |
238 | 9 | } |
239 | | |
240 | 15 | hashmap_unlock(&g_attr_hashmap); |
241 | | |
242 | 15 | return a; |
243 | 15 | } |
244 | | |
245 | | const struct git_attr *git_attr(const char *name) |
246 | 7 | { |
247 | 7 | return git_attr_internal(name, strlen(name)); |
248 | 7 | } |
249 | | |
250 | | /* What does a matched pattern decide? */ |
251 | | struct attr_state { |
252 | | const struct git_attr *attr; |
253 | | const char *setto; |
254 | | }; |
255 | | |
256 | | struct pattern { |
257 | | const char *pattern; |
258 | | int patternlen; |
259 | | int nowildcardlen; |
260 | | unsigned flags; /* PATTERN_FLAG_* */ |
261 | | }; |
262 | | |
263 | | /* |
264 | | * One rule, as from a .gitattributes file. |
265 | | * |
266 | | * If is_macro is true, then u.attr is a pointer to the git_attr being |
267 | | * defined. |
268 | | * |
269 | | * If is_macro is false, then u.pat is the filename pattern to which the |
270 | | * rule applies. |
271 | | * |
272 | | * In either case, num_attr is the number of attributes affected by |
273 | | * this rule, and state is an array listing them. The attributes are |
274 | | * listed as they appear in the file (macros unexpanded). |
275 | | */ |
276 | | struct match_attr { |
277 | | union { |
278 | | struct pattern pat; |
279 | | const struct git_attr *attr; |
280 | | } u; |
281 | | char is_macro; |
282 | | size_t num_attr; |
283 | | struct attr_state state[FLEX_ARRAY]; |
284 | | }; |
285 | | |
286 | | static const char blank[] = " \t\r\n"; |
287 | | |
288 | | /* Flags usable in read_attr() and parse_attr_line() family of functions. */ |
289 | 50.6k | #define READ_ATTR_MACRO_OK (1<<0) |
290 | 10 | #define READ_ATTR_NOFOLLOW (1<<1) |
291 | | |
292 | | /* |
293 | | * Parse a whitespace-delimited attribute state (i.e., "attr", |
294 | | * "-attr", "!attr", or "attr=value") from the string starting at src. |
295 | | * If e is not NULL, write the results to *e. Return a pointer to the |
296 | | * remainder of the string (with leading whitespace removed), or NULL |
297 | | * if there was an error. |
298 | | */ |
299 | | static const char *parse_attr(const char *src, int lineno, const char *cp, |
300 | | struct attr_state *e) |
301 | 12 | { |
302 | 12 | const char *ep, *equals; |
303 | 12 | size_t len; |
304 | | |
305 | 12 | ep = cp + strcspn(cp, blank); |
306 | 12 | equals = strchr(cp, '='); |
307 | 12 | if (equals && ep < equals) |
308 | 0 | equals = NULL; |
309 | 12 | if (equals) |
310 | 0 | len = equals - cp; |
311 | 12 | else |
312 | 12 | len = ep - cp; |
313 | 12 | if (!e) { |
314 | 6 | if (*cp == '-' || *cp == '!') { |
315 | 6 | cp++; |
316 | 6 | len--; |
317 | 6 | } |
318 | 6 | if (!attr_name_valid(cp, len)) { |
319 | 0 | report_invalid_attr(cp, len, src, lineno); |
320 | 0 | return NULL; |
321 | 0 | } |
322 | 6 | } else { |
323 | | /* |
324 | | * As this function is always called twice, once with |
325 | | * e == NULL in the first pass and then e != NULL in |
326 | | * the second pass, no need for attr_name_valid() |
327 | | * check here. |
328 | | */ |
329 | 6 | if (*cp == '-' || *cp == '!') { |
330 | 6 | e->setto = (*cp == '-') ? ATTR__FALSE : ATTR__UNSET; |
331 | 6 | cp++; |
332 | 6 | len--; |
333 | 6 | } |
334 | 0 | else if (!equals) |
335 | 0 | e->setto = ATTR__TRUE; |
336 | 0 | else { |
337 | 0 | e->setto = xmemdupz(equals + 1, ep - equals - 1); |
338 | 0 | } |
339 | 6 | e->attr = git_attr_internal(cp, len); |
340 | 6 | } |
341 | 12 | return ep + strspn(ep, blank); |
342 | 12 | } |
343 | | |
344 | | static struct match_attr *parse_attr_line(const char *line, const char *src, |
345 | | int lineno, unsigned flags) |
346 | 2 | { |
347 | 2 | size_t namelen, num_attr, i; |
348 | 2 | const char *cp, *name, *states; |
349 | 2 | struct match_attr *res = NULL; |
350 | 2 | int is_macro; |
351 | 2 | struct strbuf pattern = STRBUF_INIT; |
352 | | |
353 | 2 | cp = line + strspn(line, blank); |
354 | 2 | if (!*cp || *cp == '#') |
355 | 0 | return NULL; |
356 | 2 | name = cp; |
357 | | |
358 | 2 | if (strlen(line) >= ATTR_MAX_LINE_LENGTH) { |
359 | 0 | warning(_("ignoring overly long attributes line %d"), lineno); |
360 | 0 | return NULL; |
361 | 0 | } |
362 | | |
363 | 2 | if (*cp == '"' && !unquote_c_style(&pattern, name, &states)) { |
364 | 0 | name = pattern.buf; |
365 | 0 | namelen = pattern.len; |
366 | 2 | } else { |
367 | 2 | namelen = strcspn(name, blank); |
368 | 2 | states = name + namelen; |
369 | 2 | } |
370 | | |
371 | 2 | if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen && |
372 | 2 | starts_with(name, ATTRIBUTE_MACRO_PREFIX)) { |
373 | 2 | if (!(flags & READ_ATTR_MACRO_OK)) { |
374 | 0 | fprintf_ln(stderr, _("%s not allowed: %s:%d"), |
375 | 0 | name, src, lineno); |
376 | 0 | goto fail_return; |
377 | 0 | } |
378 | 2 | is_macro = 1; |
379 | 2 | name += strlen(ATTRIBUTE_MACRO_PREFIX); |
380 | 2 | name += strspn(name, blank); |
381 | 2 | namelen = strcspn(name, blank); |
382 | 2 | if (!attr_name_valid(name, namelen)) { |
383 | 0 | report_invalid_attr(name, namelen, src, lineno); |
384 | 0 | goto fail_return; |
385 | 0 | } |
386 | 2 | } |
387 | 0 | else |
388 | 0 | is_macro = 0; |
389 | | |
390 | 2 | states += strspn(states, blank); |
391 | | |
392 | | /* First pass to count the attr_states */ |
393 | 8 | for (cp = states, num_attr = 0; *cp; num_attr++) { |
394 | 6 | cp = parse_attr(src, lineno, cp, NULL); |
395 | 6 | if (!cp) |
396 | 0 | goto fail_return; |
397 | 6 | } |
398 | | |
399 | 2 | res = xcalloc(1, st_add3(sizeof(*res), |
400 | 2 | st_mult(sizeof(struct attr_state), num_attr), |
401 | 2 | is_macro ? 0 : namelen + 1)); |
402 | 2 | if (is_macro) { |
403 | 2 | res->u.attr = git_attr_internal(name, namelen); |
404 | 2 | } else { |
405 | 0 | char *p = (char *)&(res->state[num_attr]); |
406 | 0 | memcpy(p, name, namelen); |
407 | 0 | res->u.pat.pattern = p; |
408 | 0 | parse_path_pattern(&res->u.pat.pattern, |
409 | 0 | &res->u.pat.patternlen, |
410 | 0 | &res->u.pat.flags, |
411 | 0 | &res->u.pat.nowildcardlen); |
412 | 0 | if (res->u.pat.flags & PATTERN_FLAG_NEGATIVE) { |
413 | 0 | warning(_("Negative patterns are ignored in git attributes\n" |
414 | 0 | "Use '\\!' for literal leading exclamation.")); |
415 | 0 | goto fail_return; |
416 | 0 | } |
417 | 0 | } |
418 | 2 | res->is_macro = is_macro; |
419 | 2 | res->num_attr = num_attr; |
420 | | |
421 | | /* Second pass to fill the attr_states */ |
422 | 8 | for (cp = states, i = 0; *cp; i++) { |
423 | 6 | cp = parse_attr(src, lineno, cp, &(res->state[i])); |
424 | 6 | } |
425 | | |
426 | 2 | strbuf_release(&pattern); |
427 | 2 | return res; |
428 | | |
429 | 0 | fail_return: |
430 | 0 | strbuf_release(&pattern); |
431 | 0 | free(res); |
432 | 0 | return NULL; |
433 | 2 | } |
434 | | |
435 | | /* |
436 | | * Like info/exclude and .gitignore, the attribute information can |
437 | | * come from many places. |
438 | | * |
439 | | * (1) .gitattributes file of the same directory; |
440 | | * (2) .gitattributes file of the parent directory if (1) does not have |
441 | | * any match; this goes recursively upwards, just like .gitignore. |
442 | | * (3) $GIT_DIR/info/attributes, which overrides both of the above. |
443 | | * |
444 | | * In the same file, later entries override the earlier match, so in the |
445 | | * global list, we would have entries from info/attributes the earliest |
446 | | * (reading the file from top to bottom), .gitattributes of the root |
447 | | * directory (again, reading the file from top to bottom) down to the |
448 | | * current directory, and then scan the list backwards to find the first match. |
449 | | * This is exactly the same as what is_excluded() does in dir.c to deal with |
450 | | * .gitignore file and info/excludes file as a fallback. |
451 | | */ |
452 | | |
453 | | struct attr_stack { |
454 | | struct attr_stack *prev; |
455 | | char *origin; |
456 | | size_t originlen; |
457 | | unsigned num_matches; |
458 | | unsigned alloc; |
459 | | struct match_attr **attrs; |
460 | | }; |
461 | | |
462 | | static void attr_stack_free(struct attr_stack *e) |
463 | 0 | { |
464 | 0 | unsigned i; |
465 | 0 | free(e->origin); |
466 | 0 | for (i = 0; i < e->num_matches; i++) { |
467 | 0 | struct match_attr *a = e->attrs[i]; |
468 | 0 | size_t j; |
469 | |
|
470 | 0 | for (j = 0; j < a->num_attr; j++) { |
471 | 0 | const char *setto = a->state[j].setto; |
472 | 0 | if (setto == ATTR__TRUE || |
473 | 0 | setto == ATTR__FALSE || |
474 | 0 | setto == ATTR__UNSET || |
475 | 0 | setto == ATTR__UNKNOWN) |
476 | 0 | ; |
477 | 0 | else |
478 | 0 | free((char *) setto); |
479 | 0 | } |
480 | 0 | free(a); |
481 | 0 | } |
482 | 0 | free(e->attrs); |
483 | 0 | free(e); |
484 | 0 | } |
485 | | |
486 | | static void drop_attr_stack(struct attr_stack **stack) |
487 | 0 | { |
488 | 0 | while (*stack) { |
489 | 0 | struct attr_stack *elem = *stack; |
490 | 0 | *stack = elem->prev; |
491 | 0 | attr_stack_free(elem); |
492 | 0 | } |
493 | 0 | } |
494 | | |
495 | | /* List of all attr_check structs; access should be surrounded by mutex */ |
496 | | static struct check_vector { |
497 | | size_t nr; |
498 | | size_t alloc; |
499 | | struct attr_check **checks; |
500 | | pthread_mutex_t mutex; |
501 | | } check_vector; |
502 | | |
503 | | static inline void vector_lock(void) |
504 | 2 | { |
505 | 2 | pthread_mutex_lock(&check_vector.mutex); |
506 | 2 | } |
507 | | |
508 | | static inline void vector_unlock(void) |
509 | 2 | { |
510 | 2 | pthread_mutex_unlock(&check_vector.mutex); |
511 | 2 | } |
512 | | |
513 | | static void check_vector_add(struct attr_check *c) |
514 | 2 | { |
515 | 2 | vector_lock(); |
516 | | |
517 | 2 | ALLOC_GROW(check_vector.checks, |
518 | 2 | check_vector.nr + 1, |
519 | 2 | check_vector.alloc); |
520 | 2 | check_vector.checks[check_vector.nr++] = c; |
521 | | |
522 | 2 | vector_unlock(); |
523 | 2 | } |
524 | | |
525 | | static void check_vector_remove(struct attr_check *check) |
526 | 0 | { |
527 | 0 | int i; |
528 | |
|
529 | 0 | vector_lock(); |
530 | | |
531 | | /* Find entry */ |
532 | 0 | for (i = 0; i < check_vector.nr; i++) |
533 | 0 | if (check_vector.checks[i] == check) |
534 | 0 | break; |
535 | |
|
536 | 0 | if (i >= check_vector.nr) |
537 | 0 | BUG("no entry found"); |
538 | | |
539 | | /* shift entries over */ |
540 | 0 | for (; i < check_vector.nr - 1; i++) |
541 | 0 | check_vector.checks[i] = check_vector.checks[i + 1]; |
542 | |
|
543 | 0 | check_vector.nr--; |
544 | |
|
545 | 0 | vector_unlock(); |
546 | 0 | } |
547 | | |
548 | | /* Iterate through all attr_check instances and drop their stacks */ |
549 | | static void drop_all_attr_stacks(void) |
550 | 0 | { |
551 | 0 | int i; |
552 | |
|
553 | 0 | vector_lock(); |
554 | |
|
555 | 0 | for (i = 0; i < check_vector.nr; i++) { |
556 | 0 | drop_attr_stack(&check_vector.checks[i]->stack); |
557 | 0 | } |
558 | |
|
559 | 0 | vector_unlock(); |
560 | 0 | } |
561 | | |
562 | | struct attr_check *attr_check_alloc(void) |
563 | 2 | { |
564 | 2 | struct attr_check *c = xcalloc(1, sizeof(struct attr_check)); |
565 | | |
566 | | /* save pointer to the check struct */ |
567 | 2 | check_vector_add(c); |
568 | | |
569 | 2 | return c; |
570 | 2 | } |
571 | | |
572 | | struct attr_check *attr_check_initl(const char *one, ...) |
573 | 2 | { |
574 | 2 | struct attr_check *check; |
575 | 2 | int cnt; |
576 | 2 | va_list params; |
577 | 2 | const char *param; |
578 | | |
579 | 2 | va_start(params, one); |
580 | 7 | for (cnt = 1; (param = va_arg(params, const char *)) != NULL; cnt++) |
581 | 5 | ; |
582 | 2 | va_end(params); |
583 | | |
584 | 2 | check = attr_check_alloc(); |
585 | 2 | check->nr = cnt; |
586 | 2 | check->alloc = cnt; |
587 | 2 | CALLOC_ARRAY(check->items, cnt); |
588 | | |
589 | 2 | check->items[0].attr = git_attr(one); |
590 | 2 | va_start(params, one); |
591 | 7 | for (cnt = 1; cnt < check->nr; cnt++) { |
592 | 5 | const struct git_attr *attr; |
593 | 5 | param = va_arg(params, const char *); |
594 | 5 | if (!param) |
595 | 0 | BUG("counted %d != ended at %d", |
596 | 5 | check->nr, cnt); |
597 | 5 | attr = git_attr(param); |
598 | 5 | if (!attr) |
599 | 0 | BUG("%s: not a valid attribute name", param); |
600 | 5 | check->items[cnt].attr = attr; |
601 | 5 | } |
602 | 2 | va_end(params); |
603 | 2 | return check; |
604 | 2 | } |
605 | | |
606 | | struct attr_check *attr_check_dup(const struct attr_check *check) |
607 | 28.7k | { |
608 | 28.7k | struct attr_check *ret; |
609 | | |
610 | 28.7k | if (!check) |
611 | 28.7k | return NULL; |
612 | | |
613 | 0 | ret = attr_check_alloc(); |
614 | |
|
615 | 0 | ret->nr = check->nr; |
616 | 0 | ret->alloc = check->alloc; |
617 | 0 | DUP_ARRAY(ret->items, check->items, ret->nr); |
618 | |
|
619 | 0 | return ret; |
620 | 28.7k | } |
621 | | |
622 | | struct attr_check_item *attr_check_append(struct attr_check *check, |
623 | | const struct git_attr *attr) |
624 | 0 | { |
625 | 0 | struct attr_check_item *item; |
626 | |
|
627 | 0 | ALLOC_GROW(check->items, check->nr + 1, check->alloc); |
628 | 0 | item = &check->items[check->nr++]; |
629 | 0 | item->attr = attr; |
630 | 0 | return item; |
631 | 0 | } |
632 | | |
633 | | void attr_check_reset(struct attr_check *check) |
634 | 0 | { |
635 | 0 | check->nr = 0; |
636 | 0 | } |
637 | | |
638 | | void attr_check_clear(struct attr_check *check) |
639 | 0 | { |
640 | 0 | FREE_AND_NULL(check->items); |
641 | 0 | check->alloc = 0; |
642 | 0 | check->nr = 0; |
643 | |
|
644 | 0 | FREE_AND_NULL(check->all_attrs); |
645 | 0 | check->all_attrs_nr = 0; |
646 | |
|
647 | 0 | drop_attr_stack(&check->stack); |
648 | 0 | } |
649 | | |
650 | | void attr_check_free(struct attr_check *check) |
651 | 0 | { |
652 | 0 | if (check) { |
653 | | /* Remove check from the check vector */ |
654 | 0 | check_vector_remove(check); |
655 | |
|
656 | 0 | attr_check_clear(check); |
657 | 0 | free(check); |
658 | 0 | } |
659 | 0 | } |
660 | | |
661 | | static const char *builtin_attr[] = { |
662 | | "[attr]binary -diff -merge -text", |
663 | | NULL, |
664 | | }; |
665 | | |
666 | | static void handle_attr_line(struct attr_stack *res, |
667 | | const char *line, |
668 | | const char *src, |
669 | | int lineno, |
670 | | unsigned flags) |
671 | 2 | { |
672 | 2 | struct match_attr *a; |
673 | | |
674 | 2 | a = parse_attr_line(line, src, lineno, flags); |
675 | 2 | if (!a) |
676 | 0 | return; |
677 | 2 | ALLOC_GROW_BY(res->attrs, res->num_matches, 1, res->alloc); |
678 | 2 | res->attrs[res->num_matches - 1] = a; |
679 | 2 | } |
680 | | |
681 | | static struct attr_stack *read_attr_from_array(const char **list) |
682 | 2 | { |
683 | 2 | struct attr_stack *res; |
684 | 2 | const char *line; |
685 | 2 | int lineno = 0; |
686 | | |
687 | 2 | CALLOC_ARRAY(res, 1); |
688 | 4 | while ((line = *(list++)) != NULL) |
689 | 2 | handle_attr_line(res, line, "[builtin]", ++lineno, |
690 | 2 | READ_ATTR_MACRO_OK); |
691 | 2 | return res; |
692 | 2 | } |
693 | | |
694 | | /* |
695 | | * Callers into the attribute system assume there is a single, system-wide |
696 | | * global state where attributes are read from and when the state is flipped by |
697 | | * calling git_attr_set_direction(), the stack frames that have been |
698 | | * constructed need to be discarded so that subsequent calls into the |
699 | | * attribute system will lazily read from the right place. Since changing |
700 | | * direction causes a global paradigm shift, it should not ever be called while |
701 | | * another thread could potentially be calling into the attribute system. |
702 | | */ |
703 | | static enum git_attr_direction direction; |
704 | | |
705 | | void git_attr_set_direction(enum git_attr_direction new_direction) |
706 | 0 | { |
707 | 0 | if (is_bare_repository() && new_direction != GIT_ATTR_INDEX) |
708 | 0 | BUG("non-INDEX attr direction in a bare repo"); |
709 | | |
710 | 0 | if (new_direction != direction) |
711 | 0 | drop_all_attr_stacks(); |
712 | |
|
713 | 0 | direction = new_direction; |
714 | 0 | } |
715 | | |
716 | | static struct attr_stack *read_attr_from_file(const char *path, unsigned flags) |
717 | 8 | { |
718 | 8 | struct strbuf buf = STRBUF_INIT; |
719 | 8 | int fd; |
720 | 8 | FILE *fp; |
721 | 8 | struct attr_stack *res; |
722 | 8 | int lineno = 0; |
723 | 8 | struct stat st; |
724 | | |
725 | 8 | if (flags & READ_ATTR_NOFOLLOW) |
726 | 2 | fd = open_nofollow(path, O_RDONLY); |
727 | 6 | else |
728 | 6 | fd = open(path, O_RDONLY); |
729 | | |
730 | 8 | if (fd < 0) { |
731 | 8 | warn_on_fopen_errors(path); |
732 | 8 | return NULL; |
733 | 8 | } |
734 | 0 | fp = xfdopen(fd, "r"); |
735 | 0 | if (fstat(fd, &st)) { |
736 | 0 | warning_errno(_("cannot fstat gitattributes file '%s'"), path); |
737 | 0 | fclose(fp); |
738 | 0 | return NULL; |
739 | 0 | } |
740 | 0 | if (st.st_size >= ATTR_MAX_FILE_SIZE) { |
741 | 0 | warning(_("ignoring overly large gitattributes file '%s'"), path); |
742 | 0 | fclose(fp); |
743 | 0 | return NULL; |
744 | 0 | } |
745 | | |
746 | 0 | CALLOC_ARRAY(res, 1); |
747 | 0 | while (strbuf_getline(&buf, fp) != EOF) { |
748 | 0 | if (!lineno && starts_with(buf.buf, utf8_bom)) |
749 | 0 | strbuf_remove(&buf, 0, strlen(utf8_bom)); |
750 | 0 | handle_attr_line(res, buf.buf, path, ++lineno, flags); |
751 | 0 | } |
752 | |
|
753 | 0 | fclose(fp); |
754 | 0 | strbuf_release(&buf); |
755 | 0 | return res; |
756 | 0 | } |
757 | | |
758 | | static struct attr_stack *read_attr_from_buf(char *buf, const char *path, |
759 | | unsigned flags) |
760 | 0 | { |
761 | 0 | struct attr_stack *res; |
762 | 0 | char *sp; |
763 | 0 | int lineno = 0; |
764 | |
|
765 | 0 | if (!buf) |
766 | 0 | return NULL; |
767 | | |
768 | 0 | CALLOC_ARRAY(res, 1); |
769 | 0 | for (sp = buf; *sp;) { |
770 | 0 | char *ep; |
771 | 0 | int more; |
772 | |
|
773 | 0 | ep = strchrnul(sp, '\n'); |
774 | 0 | more = (*ep == '\n'); |
775 | 0 | *ep = '\0'; |
776 | 0 | handle_attr_line(res, sp, path, ++lineno, flags); |
777 | 0 | sp = ep + more; |
778 | 0 | } |
779 | 0 | free(buf); |
780 | |
|
781 | 0 | return res; |
782 | 0 | } |
783 | | |
784 | | static struct attr_stack *read_attr_from_blob(struct index_state *istate, |
785 | | const struct object_id *tree_oid, |
786 | | const char *path, unsigned flags) |
787 | 0 | { |
788 | 0 | struct object_id oid; |
789 | 0 | unsigned long sz; |
790 | 0 | enum object_type type; |
791 | 0 | void *buf; |
792 | 0 | unsigned short mode; |
793 | |
|
794 | 0 | if (!tree_oid) |
795 | 0 | return NULL; |
796 | | |
797 | 0 | if (get_tree_entry(istate->repo, tree_oid, path, &oid, &mode)) |
798 | 0 | return NULL; |
799 | | |
800 | 0 | buf = repo_read_object_file(istate->repo, &oid, &type, &sz); |
801 | 0 | if (!buf || type != OBJ_BLOB) { |
802 | 0 | free(buf); |
803 | 0 | return NULL; |
804 | 0 | } |
805 | | |
806 | 0 | return read_attr_from_buf(buf, path, flags); |
807 | 0 | } |
808 | | |
809 | | static struct attr_stack *read_attr_from_index(struct index_state *istate, |
810 | | const char *path, unsigned flags) |
811 | 2 | { |
812 | 2 | struct attr_stack *stack = NULL; |
813 | 2 | char *buf; |
814 | 2 | unsigned long size; |
815 | 2 | int sparse_dir_pos = -1; |
816 | | |
817 | 2 | if (!istate) |
818 | 0 | return NULL; |
819 | | |
820 | | /* |
821 | | * When handling sparse-checkouts, .gitattributes files |
822 | | * may reside within a sparse directory. We distinguish |
823 | | * whether a path exists directly in the index or not by |
824 | | * evaluating if 'pos' is negative. |
825 | | * If 'pos' is negative, the path is not directly present |
826 | | * in the index and is likely within a sparse directory. |
827 | | * For paths not in the index, The absolute value of 'pos' |
828 | | * minus 1 gives us the position where the path would be |
829 | | * inserted in lexicographic order within the index. |
830 | | * We then subtract another 1 from this value |
831 | | * (sparse_dir_pos = -pos - 2) to find the position of the |
832 | | * last index entry which is lexicographically smaller than |
833 | | * the path. This would be the sparse directory containing |
834 | | * the path. By identifying the sparse directory containing |
835 | | * the path, we can correctly read the attributes specified |
836 | | * in the .gitattributes file from the tree object of the |
837 | | * sparse directory. |
838 | | */ |
839 | 2 | if (!path_in_cone_mode_sparse_checkout(path, istate)) { |
840 | 0 | int pos = index_name_pos_sparse(istate, path, strlen(path)); |
841 | |
|
842 | 0 | if (pos < 0) |
843 | 0 | sparse_dir_pos = -pos - 2; |
844 | 0 | } |
845 | | |
846 | 2 | if (sparse_dir_pos >= 0 && |
847 | 2 | S_ISSPARSEDIR(istate->cache[sparse_dir_pos]->ce_mode) && |
848 | 2 | !strncmp(istate->cache[sparse_dir_pos]->name, path, ce_namelen(istate->cache[sparse_dir_pos]))) { |
849 | 0 | const char *relative_path = path + ce_namelen(istate->cache[sparse_dir_pos]); |
850 | 0 | stack = read_attr_from_blob(istate, &istate->cache[sparse_dir_pos]->oid, relative_path, flags); |
851 | 2 | } else { |
852 | 2 | buf = read_blob_data_from_index(istate, path, &size); |
853 | 2 | if (!buf) |
854 | 2 | return NULL; |
855 | 0 | if (size >= ATTR_MAX_FILE_SIZE) { |
856 | 0 | warning(_("ignoring overly large gitattributes blob '%s'"), path); |
857 | 0 | return NULL; |
858 | 0 | } |
859 | 0 | stack = read_attr_from_buf(buf, path, flags); |
860 | 0 | } |
861 | 0 | return stack; |
862 | 2 | } |
863 | | |
864 | | static struct attr_stack *read_attr(struct index_state *istate, |
865 | | const struct object_id *tree_oid, |
866 | | const char *path, unsigned flags) |
867 | 2 | { |
868 | 2 | struct attr_stack *res = NULL; |
869 | | |
870 | 2 | if (direction == GIT_ATTR_INDEX) { |
871 | 0 | res = read_attr_from_index(istate, path, flags); |
872 | 2 | } else if (tree_oid) { |
873 | 0 | res = read_attr_from_blob(istate, tree_oid, path, flags); |
874 | 2 | } else if (!is_bare_repository()) { |
875 | 2 | if (direction == GIT_ATTR_CHECKOUT) { |
876 | 0 | res = read_attr_from_index(istate, path, flags); |
877 | 0 | if (!res) |
878 | 0 | res = read_attr_from_file(path, flags); |
879 | 2 | } else if (direction == GIT_ATTR_CHECKIN) { |
880 | 2 | res = read_attr_from_file(path, flags); |
881 | 2 | if (!res) |
882 | | /* |
883 | | * There is no checked out .gitattributes file |
884 | | * there, but we might have it in the index. |
885 | | * We allow operation in a sparsely checked out |
886 | | * work tree, so read from it. |
887 | | */ |
888 | 2 | res = read_attr_from_index(istate, path, flags); |
889 | 2 | } |
890 | 2 | } |
891 | | |
892 | 2 | if (!res) |
893 | 2 | CALLOC_ARRAY(res, 1); |
894 | 2 | return res; |
895 | 2 | } |
896 | | |
897 | | const char *git_attr_system_file(void) |
898 | 2 | { |
899 | 2 | static const char *system_wide; |
900 | 2 | if (!system_wide) |
901 | 1 | system_wide = system_path(ETC_GITATTRIBUTES); |
902 | 2 | return system_wide; |
903 | 2 | } |
904 | | |
905 | | const char *git_attr_global_file(void) |
906 | 4 | { |
907 | 4 | if (!git_attributes_file) |
908 | 1 | git_attributes_file = xdg_config_home("attributes"); |
909 | | |
910 | 4 | return git_attributes_file; |
911 | 4 | } |
912 | | |
913 | | int git_attr_system_is_enabled(void) |
914 | 2 | { |
915 | 2 | return !git_env_bool("GIT_ATTR_NOSYSTEM", 0); |
916 | 2 | } |
917 | | |
918 | | static GIT_PATH_FUNC(git_path_info_attributes, INFOATTRIBUTES_FILE) |
919 | | |
920 | | static void push_stack(struct attr_stack **attr_stack_p, |
921 | | struct attr_stack *elem, char *origin, size_t originlen) |
922 | 50.7k | { |
923 | 50.7k | if (elem) { |
924 | 50.7k | elem->origin = origin; |
925 | 50.7k | if (origin) |
926 | 2 | elem->originlen = originlen; |
927 | 50.7k | elem->prev = *attr_stack_p; |
928 | 50.7k | *attr_stack_p = elem; |
929 | 50.7k | } |
930 | 50.7k | } |
931 | | |
932 | | static void bootstrap_attr_stack(struct index_state *istate, |
933 | | const struct object_id *tree_oid, |
934 | | struct attr_stack **stack) |
935 | 50.6k | { |
936 | 50.6k | struct attr_stack *e; |
937 | 50.6k | unsigned flags = READ_ATTR_MACRO_OK; |
938 | | |
939 | 50.6k | if (*stack) |
940 | 50.6k | return; |
941 | | |
942 | | /* builtin frame */ |
943 | 2 | e = read_attr_from_array(builtin_attr); |
944 | 2 | push_stack(stack, e, NULL, 0); |
945 | | |
946 | | /* system-wide frame */ |
947 | 2 | if (git_attr_system_is_enabled()) { |
948 | 2 | e = read_attr_from_file(git_attr_system_file(), flags); |
949 | 2 | push_stack(stack, e, NULL, 0); |
950 | 2 | } |
951 | | |
952 | | /* home directory */ |
953 | 2 | if (git_attr_global_file()) { |
954 | 2 | e = read_attr_from_file(git_attr_global_file(), flags); |
955 | 2 | push_stack(stack, e, NULL, 0); |
956 | 2 | } |
957 | | |
958 | | /* root directory */ |
959 | 2 | e = read_attr(istate, tree_oid, GITATTRIBUTES_FILE, flags | READ_ATTR_NOFOLLOW); |
960 | 2 | push_stack(stack, e, xstrdup(""), 0); |
961 | | |
962 | | /* info frame */ |
963 | 2 | if (startup_info->have_repository) |
964 | 2 | e = read_attr_from_file(git_path_info_attributes(), flags); |
965 | 0 | else |
966 | 0 | e = NULL; |
967 | 2 | if (!e) |
968 | 2 | CALLOC_ARRAY(e, 1); |
969 | 2 | push_stack(stack, e, NULL, 0); |
970 | 2 | } |
971 | | |
972 | | static void prepare_attr_stack(struct index_state *istate, |
973 | | const struct object_id *tree_oid, |
974 | | const char *path, int dirlen, |
975 | | struct attr_stack **stack) |
976 | 50.6k | { |
977 | 50.6k | struct attr_stack *info; |
978 | 50.6k | struct strbuf pathbuf = STRBUF_INIT; |
979 | | |
980 | | /* |
981 | | * At the bottom of the attribute stack is the built-in |
982 | | * set of attribute definitions, followed by the contents |
983 | | * of $(prefix)/etc/gitattributes and a file specified by |
984 | | * core.attributesfile. Then, contents from |
985 | | * .gitattributes files from directories closer to the |
986 | | * root to the ones in deeper directories are pushed |
987 | | * to the stack. Finally, at the very top of the stack |
988 | | * we always keep the contents of $GIT_DIR/info/attributes. |
989 | | * |
990 | | * When checking, we use entries from near the top of the |
991 | | * stack, preferring $GIT_DIR/info/attributes, then |
992 | | * .gitattributes in deeper directories to shallower ones, |
993 | | * and finally use the built-in set as the default. |
994 | | */ |
995 | 50.6k | bootstrap_attr_stack(istate, tree_oid, stack); |
996 | | |
997 | | /* |
998 | | * Pop the "info" one that is always at the top of the stack. |
999 | | */ |
1000 | 50.6k | info = *stack; |
1001 | 50.6k | *stack = info->prev; |
1002 | | |
1003 | | /* |
1004 | | * Pop the ones from directories that are not the prefix of |
1005 | | * the path we are checking. Break out of the loop when we see |
1006 | | * the root one (whose origin is an empty string "") or the builtin |
1007 | | * one (whose origin is NULL) without popping it. |
1008 | | */ |
1009 | 50.6k | while ((*stack)->origin) { |
1010 | 50.6k | int namelen = (*stack)->originlen; |
1011 | 50.6k | struct attr_stack *elem; |
1012 | | |
1013 | 50.6k | elem = *stack; |
1014 | 50.6k | if (namelen <= dirlen && |
1015 | 50.6k | !strncmp(elem->origin, path, namelen) && |
1016 | 50.6k | (!namelen || path[namelen] == '/')) |
1017 | 50.6k | break; |
1018 | | |
1019 | 0 | *stack = elem->prev; |
1020 | 0 | attr_stack_free(elem); |
1021 | 0 | } |
1022 | | |
1023 | | /* |
1024 | | * bootstrap_attr_stack() should have added, and the |
1025 | | * above loop should have stopped before popping, the |
1026 | | * root element whose attr_stack->origin is set to an |
1027 | | * empty string. |
1028 | | */ |
1029 | 50.6k | assert((*stack)->origin); |
1030 | | |
1031 | 50.6k | strbuf_addstr(&pathbuf, (*stack)->origin); |
1032 | | /* Build up to the directory 'path' is in */ |
1033 | 50.6k | while (pathbuf.len < dirlen) { |
1034 | 0 | size_t len = pathbuf.len; |
1035 | 0 | struct attr_stack *next; |
1036 | 0 | char *origin; |
1037 | | |
1038 | | /* Skip path-separator */ |
1039 | 0 | if (len < dirlen && is_dir_sep(path[len])) |
1040 | 0 | len++; |
1041 | | /* Find the end of the next component */ |
1042 | 0 | while (len < dirlen && !is_dir_sep(path[len])) |
1043 | 0 | len++; |
1044 | |
|
1045 | 0 | if (pathbuf.len > 0) |
1046 | 0 | strbuf_addch(&pathbuf, '/'); |
1047 | 0 | strbuf_add(&pathbuf, path + pathbuf.len, (len - pathbuf.len)); |
1048 | 0 | strbuf_addf(&pathbuf, "/%s", GITATTRIBUTES_FILE); |
1049 | |
|
1050 | 0 | next = read_attr(istate, tree_oid, pathbuf.buf, READ_ATTR_NOFOLLOW); |
1051 | | |
1052 | | /* reset the pathbuf to not include "/.gitattributes" */ |
1053 | 0 | strbuf_setlen(&pathbuf, len); |
1054 | |
|
1055 | 0 | origin = xstrdup(pathbuf.buf); |
1056 | 0 | push_stack(stack, next, origin, len); |
1057 | 0 | } |
1058 | | |
1059 | | /* |
1060 | | * Finally push the "info" one at the top of the stack. |
1061 | | */ |
1062 | 50.6k | push_stack(stack, info, NULL, 0); |
1063 | | |
1064 | 50.6k | strbuf_release(&pathbuf); |
1065 | 50.6k | } |
1066 | | |
1067 | | static int path_matches(const char *pathname, int pathlen, |
1068 | | int basename_offset, |
1069 | | const struct pattern *pat, |
1070 | | const char *base, int baselen) |
1071 | 0 | { |
1072 | 0 | const char *pattern = pat->pattern; |
1073 | 0 | int prefix = pat->nowildcardlen; |
1074 | 0 | int isdir = (pathlen && pathname[pathlen - 1] == '/'); |
1075 | |
|
1076 | 0 | if ((pat->flags & PATTERN_FLAG_MUSTBEDIR) && !isdir) |
1077 | 0 | return 0; |
1078 | | |
1079 | 0 | if (pat->flags & PATTERN_FLAG_NODIR) { |
1080 | 0 | return match_basename(pathname + basename_offset, |
1081 | 0 | pathlen - basename_offset - isdir, |
1082 | 0 | pattern, prefix, |
1083 | 0 | pat->patternlen, pat->flags); |
1084 | 0 | } |
1085 | 0 | return match_pathname(pathname, pathlen - isdir, |
1086 | 0 | base, baselen, |
1087 | 0 | pattern, prefix, pat->patternlen); |
1088 | 0 | } |
1089 | | |
1090 | | static int macroexpand_one(struct all_attrs_item *all_attrs, int nr, int rem); |
1091 | | |
1092 | | static int fill_one(struct all_attrs_item *all_attrs, |
1093 | | const struct match_attr *a, int rem) |
1094 | 0 | { |
1095 | 0 | size_t i; |
1096 | |
|
1097 | 0 | for (i = a->num_attr; rem > 0 && i > 0; i--) { |
1098 | 0 | const struct git_attr *attr = a->state[i - 1].attr; |
1099 | 0 | const char **n = &(all_attrs[attr->attr_nr].value); |
1100 | 0 | const char *v = a->state[i - 1].setto; |
1101 | |
|
1102 | 0 | if (*n == ATTR__UNKNOWN) { |
1103 | 0 | *n = v; |
1104 | 0 | rem--; |
1105 | 0 | rem = macroexpand_one(all_attrs, attr->attr_nr, rem); |
1106 | 0 | } |
1107 | 0 | } |
1108 | 0 | return rem; |
1109 | 0 | } |
1110 | | |
1111 | | static int fill(const char *path, int pathlen, int basename_offset, |
1112 | | const struct attr_stack *stack, |
1113 | | struct all_attrs_item *all_attrs, int rem) |
1114 | 50.6k | { |
1115 | 202k | for (; rem > 0 && stack; stack = stack->prev) { |
1116 | 152k | unsigned i; |
1117 | 152k | const char *base = stack->origin ? stack->origin : ""; |
1118 | | |
1119 | 202k | for (i = stack->num_matches; 0 < rem && 0 < i; i--) { |
1120 | 50.6k | const struct match_attr *a = stack->attrs[i - 1]; |
1121 | 50.6k | if (a->is_macro) |
1122 | 50.6k | continue; |
1123 | 0 | if (path_matches(path, pathlen, basename_offset, |
1124 | 0 | &a->u.pat, base, stack->originlen)) |
1125 | 0 | rem = fill_one(all_attrs, a, rem); |
1126 | 0 | } |
1127 | 152k | } |
1128 | | |
1129 | 50.6k | return rem; |
1130 | 50.6k | } |
1131 | | |
1132 | | static int macroexpand_one(struct all_attrs_item *all_attrs, int nr, int rem) |
1133 | 0 | { |
1134 | 0 | const struct all_attrs_item *item = &all_attrs[nr]; |
1135 | |
|
1136 | 0 | if (item->macro && item->value == ATTR__TRUE) |
1137 | 0 | return fill_one(all_attrs, item->macro, rem); |
1138 | 0 | else |
1139 | 0 | return rem; |
1140 | 0 | } |
1141 | | |
1142 | | /* |
1143 | | * Marks the attributes which are macros based on the attribute stack. |
1144 | | * This prevents having to search through the attribute stack each time |
1145 | | * a macro needs to be expanded during the fill stage. |
1146 | | */ |
1147 | | static void determine_macros(struct all_attrs_item *all_attrs, |
1148 | | const struct attr_stack *stack) |
1149 | 50.6k | { |
1150 | 202k | for (; stack; stack = stack->prev) { |
1151 | 152k | unsigned i; |
1152 | 202k | for (i = stack->num_matches; i > 0; i--) { |
1153 | 50.6k | const struct match_attr *ma = stack->attrs[i - 1]; |
1154 | 50.6k | if (ma->is_macro) { |
1155 | 50.6k | unsigned int n = ma->u.attr->attr_nr; |
1156 | 50.6k | if (!all_attrs[n].macro) { |
1157 | 50.6k | all_attrs[n].macro = ma; |
1158 | 50.6k | } |
1159 | 50.6k | } |
1160 | 50.6k | } |
1161 | 152k | } |
1162 | 50.6k | } |
1163 | | |
1164 | | /* |
1165 | | * Collect attributes for path into the array pointed to by check->all_attrs. |
1166 | | * If check->check_nr is non-zero, only attributes in check[] are collected. |
1167 | | * Otherwise all attributes are collected. |
1168 | | */ |
1169 | | static void collect_some_attrs(struct index_state *istate, |
1170 | | const struct object_id *tree_oid, |
1171 | | const char *path, struct attr_check *check) |
1172 | 50.6k | { |
1173 | 50.6k | int pathlen, rem, dirlen; |
1174 | 50.6k | const char *cp, *last_slash = NULL; |
1175 | 50.6k | int basename_offset; |
1176 | | |
1177 | 1.93M | for (cp = path; *cp; cp++) { |
1178 | 1.88M | if (*cp == '/' && cp[1]) |
1179 | 0 | last_slash = cp; |
1180 | 1.88M | } |
1181 | 50.6k | pathlen = cp - path; |
1182 | 50.6k | if (last_slash) { |
1183 | 0 | basename_offset = last_slash + 1 - path; |
1184 | 0 | dirlen = last_slash - path; |
1185 | 50.6k | } else { |
1186 | 50.6k | basename_offset = 0; |
1187 | 50.6k | dirlen = 0; |
1188 | 50.6k | } |
1189 | | |
1190 | 50.6k | prepare_attr_stack(istate, tree_oid, path, dirlen, &check->stack); |
1191 | 50.6k | all_attrs_init(&g_attr_hashmap, check); |
1192 | 50.6k | determine_macros(check->all_attrs, check->stack); |
1193 | | |
1194 | 50.6k | rem = check->all_attrs_nr; |
1195 | 50.6k | fill(path, pathlen, basename_offset, check->stack, check->all_attrs, rem); |
1196 | 50.6k | } |
1197 | | |
1198 | | static const char *default_attr_source_tree_object_name; |
1199 | | static int ignore_bad_attr_tree; |
1200 | | |
1201 | | void set_git_attr_source(const char *tree_object_name) |
1202 | 0 | { |
1203 | 0 | default_attr_source_tree_object_name = xstrdup(tree_object_name); |
1204 | 0 | } |
1205 | | |
1206 | | static void compute_default_attr_source(struct object_id *attr_source) |
1207 | 50.6k | { |
1208 | 50.6k | if (!default_attr_source_tree_object_name) |
1209 | 50.6k | default_attr_source_tree_object_name = getenv(GIT_ATTR_SOURCE_ENVIRONMENT); |
1210 | | |
1211 | 50.6k | if (!default_attr_source_tree_object_name && git_attr_tree) { |
1212 | 0 | default_attr_source_tree_object_name = git_attr_tree; |
1213 | 0 | ignore_bad_attr_tree = 1; |
1214 | 0 | } |
1215 | | |
1216 | 50.6k | if (!default_attr_source_tree_object_name && |
1217 | 50.6k | startup_info->have_repository && |
1218 | 50.6k | is_bare_repository()) { |
1219 | 0 | default_attr_source_tree_object_name = "HEAD"; |
1220 | 0 | ignore_bad_attr_tree = 1; |
1221 | 0 | } |
1222 | | |
1223 | 50.6k | if (!default_attr_source_tree_object_name || !is_null_oid(attr_source)) |
1224 | 50.6k | return; |
1225 | | |
1226 | 0 | if (repo_get_oid_treeish(the_repository, |
1227 | 0 | default_attr_source_tree_object_name, |
1228 | 0 | attr_source) && !ignore_bad_attr_tree) |
1229 | 0 | die(_("bad --attr-source or GIT_ATTR_SOURCE")); |
1230 | 0 | } |
1231 | | |
1232 | | static struct object_id *default_attr_source(void) |
1233 | 50.6k | { |
1234 | 50.6k | static struct object_id attr_source; |
1235 | | |
1236 | 50.6k | if (is_null_oid(&attr_source)) |
1237 | 50.6k | compute_default_attr_source(&attr_source); |
1238 | 50.6k | if (is_null_oid(&attr_source)) |
1239 | 50.6k | return NULL; |
1240 | 0 | return &attr_source; |
1241 | 50.6k | } |
1242 | | |
1243 | | void git_check_attr(struct index_state *istate, |
1244 | | const char *path, |
1245 | | struct attr_check *check) |
1246 | 50.6k | { |
1247 | 50.6k | int i; |
1248 | 50.6k | const struct object_id *tree_oid = default_attr_source(); |
1249 | | |
1250 | 50.6k | collect_some_attrs(istate, tree_oid, path, check); |
1251 | | |
1252 | 270k | for (i = 0; i < check->nr; i++) { |
1253 | 219k | unsigned int n = check->items[i].attr->attr_nr; |
1254 | 219k | const char *value = check->all_attrs[n].value; |
1255 | 219k | if (value == ATTR__UNKNOWN) |
1256 | 219k | value = ATTR__UNSET; |
1257 | 219k | check->items[i].value = value; |
1258 | 219k | } |
1259 | 50.6k | } |
1260 | | |
1261 | | void git_all_attrs(struct index_state *istate, |
1262 | | const char *path, struct attr_check *check) |
1263 | 0 | { |
1264 | 0 | int i; |
1265 | 0 | const struct object_id *tree_oid = default_attr_source(); |
1266 | |
|
1267 | 0 | attr_check_reset(check); |
1268 | 0 | collect_some_attrs(istate, tree_oid, path, check); |
1269 | |
|
1270 | 0 | for (i = 0; i < check->all_attrs_nr; i++) { |
1271 | 0 | const char *name = check->all_attrs[i].attr->name; |
1272 | 0 | const char *value = check->all_attrs[i].value; |
1273 | 0 | struct attr_check_item *item; |
1274 | 0 | if (value == ATTR__UNSET || value == ATTR__UNKNOWN) |
1275 | 0 | continue; |
1276 | 0 | item = attr_check_append(check, git_attr(name)); |
1277 | 0 | item->value = value; |
1278 | 0 | } |
1279 | 0 | } |
1280 | | |
1281 | | void attr_start(void) |
1282 | 0 | { |
1283 | 0 | pthread_mutex_init(&g_attr_hashmap.mutex, NULL); |
1284 | 0 | pthread_mutex_init(&check_vector.mutex, NULL); |
1285 | 0 | } |