4 #define ATTR__UNKNOWN ((void *) -2)
7 * The basic design decision here is that we are not going to have
8 * insanely large number of attributes.
10 * This is a randomly chosen prime.
19 struct git_attr
*next
;
22 char name
[FLEX_ARRAY
];
26 static struct git_attr_check
*check_all_attr
;
27 static struct git_attr
*(git_attr_hash
[HASHSIZE
]);
29 static unsigned hash_name(const char *name
, int namelen
)
36 val
= ((val
<< 7) | (val
>> 22)) ^ c
;
41 static int invalid_attr_name(const char *name
, int namelen
)
44 * Attribute name cannot begin with '-' and from
45 * [-A-Za-z0-9_.]. We'd specifically exclude '=' for now,
46 * as we might later want to allow non-binary value for
47 * attributes, e.g. "*.svg merge=special-merge-program-for-svg"
53 if (! (ch
== '-' || ch
== '.' || ch
== '_' ||
54 ('0' <= ch
&& ch
<= '9') ||
55 ('a' <= ch
&& ch
<= 'z') ||
56 ('A' <= ch
&& ch
<= 'Z')) )
62 struct git_attr
*git_attr(const char *name
, int len
)
64 unsigned hval
= hash_name(name
, len
);
65 unsigned pos
= hval
% HASHSIZE
;
68 for (a
= git_attr_hash
[pos
]; a
; a
= a
->next
) {
70 !memcmp(a
->name
, name
, len
) && !a
->name
[len
])
74 if (invalid_attr_name(name
, len
))
77 a
= xmalloc(sizeof(*a
) + len
+ 1);
78 memcpy(a
->name
, name
, len
);
81 a
->next
= git_attr_hash
[pos
];
82 a
->attr_nr
= attr_nr
++;
83 git_attr_hash
[pos
] = a
;
85 check_all_attr
= xrealloc(check_all_attr
,
86 sizeof(*check_all_attr
) * attr_nr
);
87 check_all_attr
[a
->attr_nr
].attr
= a
;
88 check_all_attr
[a
->attr_nr
].value
= ATTR__UNKNOWN
;
93 * .gitattributes file is one line per record, each of which is
97 * (3) whitespace separated list of attribute names, each of which
98 * could be prefixed with '-' to mean "set to false", '!' to mean
102 /* What does a matched pattern decide? */
104 struct git_attr
*attr
;
111 struct git_attr
*attr
;
115 struct attr_state state
[FLEX_ARRAY
];
118 static const char blank
[] = " \t\r\n";
120 static const char *parse_attr(const char *src
, int lineno
, const char *cp
,
121 int *num_attr
, struct match_attr
*res
)
123 const char *ep
, *equals
;
126 ep
= cp
+ strcspn(cp
, blank
);
127 equals
= strchr(cp
, '=');
128 if (equals
&& ep
< equals
)
135 if (*cp
== '-' || *cp
== '!') {
139 if (invalid_attr_name(cp
, len
)) {
141 "%.*s is not a valid attribute name: %s:%d\n",
142 len
, cp
, src
, lineno
);
146 struct attr_state
*e
;
148 e
= &(res
->state
[*num_attr
]);
149 if (*cp
== '-' || *cp
== '!') {
150 e
->setto
= (*cp
== '-') ? ATTR__FALSE
: ATTR__UNSET
;
155 e
->setto
= ATTR__TRUE
;
158 int vallen
= ep
- equals
;
159 value
= xmalloc(vallen
);
160 memcpy(value
, equals
+1, vallen
-1);
164 e
->attr
= git_attr(cp
, len
);
167 return ep
+ strspn(ep
, blank
);
170 static struct match_attr
*parse_attr_line(const char *line
, const char *src
,
171 int lineno
, int macro_ok
)
175 const char *cp
, *name
;
176 struct match_attr
*res
= NULL
;
180 cp
= line
+ strspn(line
, blank
);
181 if (!*cp
|| *cp
== '#')
184 namelen
= strcspn(name
, blank
);
185 if (strlen(ATTRIBUTE_MACRO_PREFIX
) < namelen
&&
186 !prefixcmp(name
, ATTRIBUTE_MACRO_PREFIX
)) {
188 fprintf(stderr
, "%s not allowed: %s:%d\n",
193 name
+= strlen(ATTRIBUTE_MACRO_PREFIX
);
194 name
+= strspn(name
, blank
);
195 namelen
= strcspn(name
, blank
);
196 if (invalid_attr_name(name
, namelen
)) {
198 "%.*s is not a valid attribute name: %s:%d\n",
199 namelen
, name
, src
, lineno
);
206 for (pass
= 0; pass
< 2; pass
++) {
207 /* pass 0 counts and allocates, pass 1 fills */
210 cp
= cp
+ strspn(cp
, blank
);
212 cp
= parse_attr(src
, lineno
, cp
, &num_attr
, res
);
217 sizeof(struct attr_state
) * num_attr
+
218 (is_macro
? 0 : namelen
+ 1));
220 res
->u
.attr
= git_attr(name
, namelen
);
222 res
->u
.pattern
= (char*)&(res
->state
[num_attr
]);
223 memcpy(res
->u
.pattern
, name
, namelen
);
224 res
->u
.pattern
[namelen
] = 0;
226 res
->is_macro
= is_macro
;
227 res
->num_attr
= num_attr
;
233 * Like info/exclude and .gitignore, the attribute information can
234 * come from many places.
236 * (1) .gitattribute file of the same directory;
237 * (2) .gitattribute file of the parent directory if (1) does not have
238 * any match; this goes recursively upwards, just like .gitignore.
239 * (3) $GIT_DIR/info/attributes, which overrides both of the above.
241 * In the same file, later entries override the earlier match, so in the
242 * global list, we would have entries from info/attributes the earliest
243 * (reading the file from top to bottom), .gitattribute of the root
244 * directory (again, reading the file from top to bottom) down to the
245 * current directory, and then scan the list backwards to find the first match.
246 * This is exactly the same as what excluded() does in dir.c to deal with
250 static struct attr_stack
{
251 struct attr_stack
*prev
;
253 unsigned num_matches
;
254 struct match_attr
**attrs
;
257 static void free_attr_elem(struct attr_stack
*e
)
261 for (i
= 0; i
< e
->num_matches
; i
++) {
262 struct match_attr
*a
= e
->attrs
[i
];
264 for (j
= 0; j
< a
->num_attr
; j
++) {
265 void *setto
= a
->state
[j
].setto
;
266 if (setto
== ATTR__TRUE
||
267 setto
== ATTR__FALSE
||
268 setto
== ATTR__UNSET
||
269 setto
== ATTR__UNKNOWN
)
279 static const char *builtin_attr
[] = {
280 "[attr]binary -diff -crlf",
284 static struct attr_stack
*read_attr_from_array(const char **list
)
286 struct attr_stack
*res
;
290 res
= xcalloc(1, sizeof(*res
));
291 while ((line
= *(list
++)) != NULL
) {
292 struct match_attr
*a
;
294 a
= parse_attr_line(line
, "[builtin]", ++lineno
, 1);
297 res
->attrs
= xrealloc(res
->attrs
, res
->num_matches
+ 1);
298 res
->attrs
[res
->num_matches
++] = a
;
303 static struct attr_stack
*read_attr_from_file(const char *path
, int macro_ok
)
306 struct attr_stack
*res
;
310 res
= xcalloc(1, sizeof(*res
));
311 fp
= fopen(path
, "r");
315 while (fgets(buf
, sizeof(buf
), fp
)) {
316 struct match_attr
*a
;
318 a
= parse_attr_line(buf
, path
, ++lineno
, macro_ok
);
321 res
->attrs
= xrealloc(res
->attrs
, res
->num_matches
+ 1);
322 res
->attrs
[res
->num_matches
++] = a
;
329 static void debug_info(const char *what
, struct attr_stack
*elem
)
331 fprintf(stderr
, "%s: %s\n", what
, elem
->origin
? elem
->origin
: "()");
333 static void debug_set(const char *what
, const char *match
, struct git_attr
*attr
, void *v
)
335 const char *value
= v
;
337 if (ATTR_TRUE(value
))
339 else if (ATTR_FALSE(value
))
341 else if (ATTR_UNSET(value
))
342 value
= "unspecified";
344 fprintf(stderr
, "%s: %s => %s (%s)\n",
345 what
, attr
->name
, (char *) value
, match
);
347 #define debug_push(a) debug_info("push", (a))
348 #define debug_pop(a) debug_info("pop", (a))
350 #define debug_push(a) do { ; } while (0)
351 #define debug_pop(a) do { ; } while (0)
352 #define debug_set(a,b,c,d) do { ; } while (0)
355 static void bootstrap_attr_stack(void)
358 struct attr_stack
*elem
;
360 elem
= read_attr_from_array(builtin_attr
);
362 elem
->prev
= attr_stack
;
365 elem
= read_attr_from_file(GITATTRIBUTES_FILE
, 1);
366 elem
->origin
= strdup("");
367 elem
->prev
= attr_stack
;
371 elem
= read_attr_from_file(git_path(INFOATTRIBUTES_FILE
), 1);
373 elem
->prev
= attr_stack
;
378 static void prepare_attr_stack(const char *path
, int dirlen
)
380 struct attr_stack
*elem
, *info
;
382 char pathbuf
[PATH_MAX
];
385 * At the bottom of the attribute stack is the built-in
386 * set of attribute definitions. Then, contents from
387 * .gitattribute files from directories closer to the
388 * root to the ones in deeper directories are pushed
389 * to the stack. Finally, at the very top of the stack
390 * we always keep the contents of $GIT_DIR/info/attributes.
392 * When checking, we use entries from near the top of the
393 * stack, preferring $GIT_DIR/info/attributes, then
394 * .gitattributes in deeper directories to shallower ones,
395 * and finally use the built-in set as the default.
398 bootstrap_attr_stack();
401 * Pop the "info" one that is always at the top of the stack.
404 attr_stack
= info
->prev
;
407 * Pop the ones from directories that are not the prefix of
408 * the path we are checking.
410 while (attr_stack
&& attr_stack
->origin
) {
411 int namelen
= strlen(attr_stack
->origin
);
414 if (namelen
<= dirlen
&&
415 !strncmp(elem
->origin
, path
, namelen
))
419 attr_stack
= elem
->prev
;
420 free_attr_elem(elem
);
424 * Read from parent directories and push them down
429 len
= strlen(attr_stack
->origin
);
432 memcpy(pathbuf
, path
, dirlen
);
433 memcpy(pathbuf
+ dirlen
, "/", 2);
434 cp
= strchr(pathbuf
+ len
+ 1, '/');
435 strcpy(cp
+ 1, GITATTRIBUTES_FILE
);
436 elem
= read_attr_from_file(pathbuf
, 0);
438 elem
->origin
= strdup(pathbuf
);
439 elem
->prev
= attr_stack
;
445 * Finally push the "info" one at the top of the stack.
447 info
->prev
= attr_stack
;
451 static int path_matches(const char *pathname
, int pathlen
,
453 const char *base
, int baselen
)
455 if (!strchr(pattern
, '/')) {
457 const char *basename
= strrchr(pathname
, '/');
458 basename
= basename
? basename
+ 1 : pathname
;
459 return (fnmatch(pattern
, basename
, 0) == 0);
462 * match with FNM_PATHNAME; the pattern has base implicitly
467 if (pathlen
< baselen
||
468 (baselen
&& pathname
[baselen
- 1] != '/') ||
469 strncmp(pathname
, base
, baselen
))
471 return fnmatch(pattern
, pathname
+ baselen
, FNM_PATHNAME
) == 0;
474 static int fill_one(const char *what
, struct match_attr
*a
, int rem
)
476 struct git_attr_check
*check
= check_all_attr
;
479 for (i
= 0; 0 < rem
&& i
< a
->num_attr
; i
++) {
480 struct git_attr
*attr
= a
->state
[i
].attr
;
481 void **n
= &(check
[attr
->attr_nr
].value
);
482 void *v
= a
->state
[i
].setto
;
484 if (*n
== ATTR__UNKNOWN
) {
485 debug_set(what
, a
->u
.pattern
, attr
, v
);
493 static int fill(const char *path
, int pathlen
, struct attr_stack
*stk
, int rem
)
496 const char *base
= stk
->origin
? stk
->origin
: "";
498 for (i
= stk
->num_matches
- 1; 0 < rem
&& 0 <= i
; i
--) {
499 struct match_attr
*a
= stk
->attrs
[i
];
502 if (path_matches(path
, pathlen
,
503 a
->u
.pattern
, base
, strlen(base
)))
504 rem
= fill_one("fill", a
, rem
);
509 static int macroexpand(struct attr_stack
*stk
, int rem
)
512 struct git_attr_check
*check
= check_all_attr
;
514 for (i
= stk
->num_matches
- 1; 0 < rem
&& 0 <= i
; i
--) {
515 struct match_attr
*a
= stk
->attrs
[i
];
518 if (check
[a
->u
.attr
->attr_nr
].value
!= ATTR__TRUE
)
520 rem
= fill_one("expand", a
, rem
);
525 int git_checkattr(const char *path
, int num
, struct git_attr_check
*check
)
527 struct attr_stack
*stk
;
529 int dirlen
, pathlen
, i
, rem
;
531 bootstrap_attr_stack();
532 for (i
= 0; i
< attr_nr
; i
++)
533 check_all_attr
[i
].value
= ATTR__UNKNOWN
;
535 pathlen
= strlen(path
);
536 cp
= strrchr(path
, '/');
541 prepare_attr_stack(path
, dirlen
);
543 for (stk
= attr_stack
; 0 < rem
&& stk
; stk
= stk
->prev
)
544 rem
= fill(path
, pathlen
, stk
, rem
);
546 for (stk
= attr_stack
; 0 < rem
&& stk
; stk
= stk
->prev
)
547 rem
= macroexpand(stk
, rem
);
549 for (i
= 0; i
< num
; i
++) {
550 void *value
= check_all_attr
[check
[i
].attr
->attr_nr
].value
;
551 if (value
== ATTR__UNKNOWN
)
553 check
[i
].value
= value
;