11#include "cache.h"
22#include "attr.h"
33
4+ #define ATTR__UNKNOWN ((void *) -2)
5+
46/*
57 * The basic design decision here is that we are not going to have
68 * insanely large number of attributes.
@@ -83,6 +85,7 @@ struct git_attr *git_attr(const char *name, int len)
8385 check_all_attr = xrealloc (check_all_attr ,
8486 sizeof (* check_all_attr ) * attr_nr );
8587 check_all_attr [a -> attr_nr ].attr = a ;
88+ check_all_attr [a -> attr_nr ].value = ATTR__UNKNOWN ;
8689 return a ;
8790}
8891
@@ -92,12 +95,14 @@ struct git_attr *git_attr(const char *name, int len)
9295 * (1) glob pattern.
9396 * (2) whitespace
9497 * (3) whitespace separated list of attribute names, each of which
95- * could be prefixed with '-' to mean "not set".
98+ * could be prefixed with '-' to mean "set to false", '!' to mean
99+ * "unset".
96100 */
97101
102+ /* What does a matched pattern decide? */
98103struct attr_state {
99- int unset ;
100104 struct git_attr * attr ;
105+ void * setto ;
101106};
102107
103108struct match_attr {
@@ -112,13 +117,63 @@ struct match_attr {
112117
113118static const char blank [] = " \t\r\n" ;
114119
120+ static const char * parse_attr (const char * src , int lineno , const char * cp ,
121+ int * num_attr , struct match_attr * res )
122+ {
123+ const char * ep , * equals ;
124+ int len ;
125+
126+ ep = cp + strcspn (cp , blank );
127+ equals = strchr (cp , '=' );
128+ if (equals && ep < equals )
129+ equals = NULL ;
130+ if (equals )
131+ len = equals - cp ;
132+ else
133+ len = ep - cp ;
134+ if (!res ) {
135+ if (* cp == '-' || * cp == '!' ) {
136+ cp ++ ;
137+ len -- ;
138+ }
139+ if (invalid_attr_name (cp , len )) {
140+ fprintf (stderr ,
141+ "%.*s is not a valid attribute name: %s:%d\n" ,
142+ len , cp , src , lineno );
143+ return NULL ;
144+ }
145+ } else {
146+ struct attr_state * e ;
147+
148+ e = & (res -> state [* num_attr ]);
149+ if (* cp == '-' || * cp == '!' ) {
150+ e -> setto = (* cp == '-' ) ? ATTR__FALSE : ATTR__UNSET ;
151+ cp ++ ;
152+ len -- ;
153+ }
154+ else if (!equals )
155+ e -> setto = ATTR__TRUE ;
156+ else {
157+ char * value ;
158+ int vallen = ep - equals ;
159+ value = xmalloc (vallen );
160+ memcpy (value , equals + 1 , vallen - 1 );
161+ value [vallen - 1 ] = 0 ;
162+ e -> setto = value ;
163+ }
164+ e -> attr = git_attr (cp , len );
165+ }
166+ (* num_attr )++ ;
167+ return ep + strspn (ep , blank );
168+ }
169+
115170static struct match_attr * parse_attr_line (const char * line , const char * src ,
116171 int lineno , int macro_ok )
117172{
118173 int namelen ;
119174 int num_attr ;
120175 const char * cp , * name ;
121- struct match_attr * res = res ;
176+ struct match_attr * res = NULL ;
122177 int pass ;
123178 int is_macro ;
124179
@@ -153,42 +208,16 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
153208 num_attr = 0 ;
154209 cp = name + namelen ;
155210 cp = cp + strspn (cp , blank );
156- while (* cp ) {
157- const char * ep ;
158- ep = cp + strcspn (cp , blank );
159- if (!pass ) {
160- if (* cp == '-' )
161- cp ++ ;
162- if (invalid_attr_name (cp , ep - cp )) {
163- fprintf (stderr ,
164- "%.*s is not a valid attribute name: %s:%d\n" ,
165- (int )(ep - cp ), cp ,
166- src , lineno );
167- return NULL ;
168- }
169- } else {
170- struct attr_state * e ;
171-
172- e = & (res -> state [num_attr ]);
173- if (* cp == '-' ) {
174- e -> unset = 1 ;
175- cp ++ ;
176- }
177- e -> attr = git_attr (cp , ep - cp );
178- }
179- num_attr ++ ;
180- cp = ep + strspn (ep , blank );
181- }
211+ while (* cp )
212+ cp = parse_attr (src , lineno , cp , & num_attr , res );
182213 if (pass )
183214 break ;
184-
185215 res = xcalloc (1 ,
186216 sizeof (* res ) +
187217 sizeof (struct attr_state ) * num_attr +
188218 (is_macro ? 0 : namelen + 1 ));
189- if (is_macro ) {
219+ if (is_macro )
190220 res -> u .attr = git_attr (name , namelen );
191- }
192221 else {
193222 res -> u .pattern = (char * )& (res -> state [num_attr ]);
194223 memcpy (res -> u .pattern , name , namelen );
@@ -205,9 +234,9 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
205234 * come from many places.
206235 *
207236 * (1) .gitattribute file of the same directory;
208- * (2) .gitattribute file of the parent directory if (1) does not have any match;
209- * this goes recursively upwards, just like .gitignore
210- * (3) perhaps $GIT_DIR/info/attributes, as the final fallback .
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 .
211240 *
212241 * In the same file, later entries override the earlier match, so in the
213242 * global list, we would have entries from info/attributes the earliest
@@ -229,8 +258,21 @@ static void free_attr_elem(struct attr_stack *e)
229258{
230259 int i ;
231260 free (e -> origin );
232- for (i = 0 ; i < e -> num_matches ; i ++ )
233- free (e -> attrs [i ]);
261+ for (i = 0 ; i < e -> num_matches ; i ++ ) {
262+ struct match_attr * a = e -> attrs [i ];
263+ int j ;
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 )
270+ ;
271+ else
272+ free (setto );
273+ }
274+ free (a );
275+ }
234276 free (e );
235277}
236278
@@ -288,10 +330,19 @@ static void debug_info(const char *what, struct attr_stack *elem)
288330{
289331 fprintf (stderr , "%s: %s\n" , what , elem -> origin ? elem -> origin : "()" );
290332}
291- static void debug_set (const char * what , const char * match , struct git_attr * attr , int set )
333+ static void debug_set (const char * what , const char * match , struct git_attr * attr , void * v )
292334{
293- fprintf (stderr , "%s: %s => %d (%s)\n" ,
294- what , attr -> name , set , match );
335+ const char * value = v ;
336+
337+ if (ATTR_TRUE (value ))
338+ value = "set" ;
339+ else if (ATTR_FALSE (value ))
340+ value = "unset" ;
341+ else if (ATTR_UNSET (value ))
342+ value = "unspecified" ;
343+
344+ fprintf (stderr , "%s: %s => %s (%s)\n" ,
345+ what , attr -> name , (char * ) value , match );
295346}
296347#define debug_push (a ) debug_info("push", (a))
297348#define debug_pop (a ) debug_info("pop", (a))
@@ -420,56 +471,53 @@ static int path_matches(const char *pathname, int pathlen,
420471 return fnmatch (pattern , pathname + baselen , FNM_PATHNAME ) == 0 ;
421472}
422473
474+ static int fill_one (const char * what , struct match_attr * a , int rem )
475+ {
476+ struct git_attr_check * check = check_all_attr ;
477+ int i ;
478+
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 ;
483+
484+ if (* n == ATTR__UNKNOWN ) {
485+ debug_set (what , a -> u .pattern , attr , v );
486+ * n = v ;
487+ rem -- ;
488+ }
489+ }
490+ return rem ;
491+ }
492+
423493static int fill (const char * path , int pathlen , struct attr_stack * stk , int rem )
424494{
495+ int i ;
425496 const char * base = stk -> origin ? stk -> origin : "" ;
426- int i , j ;
427- struct git_attr_check * check = check_all_attr ;
428497
429498 for (i = stk -> num_matches - 1 ; 0 < rem && 0 <= i ; i -- ) {
430499 struct match_attr * a = stk -> attrs [i ];
431500 if (a -> is_macro )
432501 continue ;
433502 if (path_matches (path , pathlen ,
434- a -> u .pattern , base , strlen (base ))) {
435- for (j = 0 ; 0 < rem && j < a -> num_attr ; j ++ ) {
436- struct git_attr * attr = a -> state [j ].attr ;
437- int set = !a -> state [j ].unset ;
438- int * n = & (check [attr -> attr_nr ].isset );
439-
440- if (* n < 0 ) {
441- debug_set ("fill" , a -> u .pattern , attr , set );
442- * n = set ;
443- rem -- ;
444- }
445- }
446- }
503+ a -> u .pattern , base , strlen (base )))
504+ rem = fill_one ("fill" , a , rem );
447505 }
448506 return rem ;
449507}
450508
451509static int macroexpand (struct attr_stack * stk , int rem )
452510{
453- int i , j ;
511+ int i ;
454512 struct git_attr_check * check = check_all_attr ;
455513
456514 for (i = stk -> num_matches - 1 ; 0 < rem && 0 <= i ; i -- ) {
457515 struct match_attr * a = stk -> attrs [i ];
458516 if (!a -> is_macro )
459517 continue ;
460- if (check [a -> u .attr -> attr_nr ].isset < 0 )
518+ if (check [a -> u .attr -> attr_nr ].value != ATTR__TRUE )
461519 continue ;
462- for (j = 0 ; 0 < rem && j < a -> num_attr ; j ++ ) {
463- struct git_attr * attr = a -> state [j ].attr ;
464- int set = !a -> state [j ].unset ;
465- int * n = & (check [attr -> attr_nr ].isset );
466-
467- if (* n < 0 ) {
468- debug_set ("expand" , a -> u .attr -> name , attr , set );
469- * n = set ;
470- rem -- ;
471- }
472- }
520+ rem = fill_one ("expand" , a , rem );
473521 }
474522 return rem ;
475523}
@@ -482,7 +530,7 @@ int git_checkattr(const char *path, int num, struct git_attr_check *check)
482530
483531 bootstrap_attr_stack ();
484532 for (i = 0 ; i < attr_nr ; i ++ )
485- check_all_attr [i ].isset = -1 ;
533+ check_all_attr [i ].value = ATTR__UNKNOWN ;
486534
487535 pathlen = strlen (path );
488536 cp = strrchr (path , '/' );
@@ -498,8 +546,12 @@ int git_checkattr(const char *path, int num, struct git_attr_check *check)
498546 for (stk = attr_stack ; 0 < rem && stk ; stk = stk -> prev )
499547 rem = macroexpand (stk , rem );
500548
501- for (i = 0 ; i < num ; i ++ )
502- check [i ].isset = check_all_attr [check [i ].attr -> attr_nr ].isset ;
549+ for (i = 0 ; i < num ; i ++ ) {
550+ void * value = check_all_attr [check [i ].attr -> attr_nr ].value ;
551+ if (value == ATTR__UNKNOWN )
552+ value = ATTR__UNSET ;
553+ check [i ].value = value ;
554+ }
503555
504556 return 0 ;
505557}
0 commit comments