1+ /*
2+ * Handle git attributes. See gitattributes(5) for a description of
3+ * the file syntax, and Documentation/technical/api-gitattributes.txt
4+ * for a description of the API.
5+ *
6+ * One basic design decision here is that we are not going to support
7+ * an insanely large number of attributes.
8+ */
9+
110#define NO_THE_INDEX_COMPATIBILITY_MACROS
211#include "cache.h"
312#include "exec_cmd.h"
@@ -13,12 +22,7 @@ static const char git_attr__unknown[] = "(builtin)unknown";
1322
1423static const char * attributes_file ;
1524
16- /*
17- * The basic design decision here is that we are not going to have
18- * insanely large number of attributes.
19- *
20- * This is a randomly chosen prime.
21- */
25+ /* This is a randomly chosen prime. */
2226#define HASHSIZE 257
2327
2428#ifndef DEBUG_ATTR
@@ -106,22 +110,26 @@ struct git_attr *git_attr(const char *name)
106110 return git_attr_internal (name , strlen (name ));
107111}
108112
109- /*
110- * .gitattributes file is one line per record, each of which is
111- *
112- * (1) glob pattern.
113- * (2) whitespace
114- * (3) whitespace separated list of attribute names, each of which
115- * could be prefixed with '-' to mean "set to false", '!' to mean
116- * "unset".
117- */
118-
119113/* What does a matched pattern decide? */
120114struct attr_state {
121115 struct git_attr * attr ;
122116 const char * setto ;
123117};
124118
119+ /*
120+ * One rule, as from a .gitattributes file.
121+ *
122+ * If is_macro is true, then u.attr is a pointer to the git_attr being
123+ * defined.
124+ *
125+ * If is_macro is false, then u.pattern points at the filename pattern
126+ * to which the rule applies. (The memory pointed to is part of the
127+ * memory block allocated for the match_attr instance.)
128+ *
129+ * In either case, num_attr is the number of attributes affected by
130+ * this rule, and state is an array listing them. The attributes are
131+ * listed as they appear in the file (macros unexpanded).
132+ */
125133struct match_attr {
126134 union {
127135 char * pattern ;
@@ -134,8 +142,15 @@ struct match_attr {
134142
135143static const char blank [] = " \t\r\n" ;
136144
145+ /*
146+ * Parse a whitespace-delimited attribute state (i.e., "attr",
147+ * "-attr", "!attr", or "attr=value") from the string starting at src.
148+ * If e is not NULL, write the results to *e. Return a pointer to the
149+ * remainder of the string (with leading whitespace removed), or NULL
150+ * if there was an error.
151+ */
137152static const char * parse_attr (const char * src , int lineno , const char * cp ,
138- int * num_attr , struct match_attr * res )
153+ struct attr_state * e )
139154{
140155 const char * ep , * equals ;
141156 int len ;
@@ -148,7 +163,7 @@ static const char *parse_attr(const char *src, int lineno, const char *cp,
148163 len = equals - cp ;
149164 else
150165 len = ep - cp ;
151- if (!res ) {
166+ if (!e ) {
152167 if (* cp == '-' || * cp == '!' ) {
153168 cp ++ ;
154169 len -- ;
@@ -160,9 +175,6 @@ static const char *parse_attr(const char *src, int lineno, const char *cp,
160175 return NULL ;
161176 }
162177 } else {
163- struct attr_state * e ;
164-
165- e = & (res -> state [* num_attr ]);
166178 if (* cp == '-' || * cp == '!' ) {
167179 e -> setto = (* cp == '-' ) ? ATTR__FALSE : ATTR__UNSET ;
168180 cp ++ ;
@@ -175,18 +187,16 @@ static const char *parse_attr(const char *src, int lineno, const char *cp,
175187 }
176188 e -> attr = git_attr_internal (cp , len );
177189 }
178- (* num_attr )++ ;
179190 return ep + strspn (ep , blank );
180191}
181192
182193static struct match_attr * parse_attr_line (const char * line , const char * src ,
183194 int lineno , int macro_ok )
184195{
185196 int namelen ;
186- int num_attr ;
187- const char * cp , * name ;
197+ int num_attr , i ;
198+ const char * cp , * name , * states ;
188199 struct match_attr * res = NULL ;
189- int pass ;
190200 int is_macro ;
191201
192202 cp = line + strspn (line , blank );
@@ -215,32 +225,35 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
215225 else
216226 is_macro = 0 ;
217227
218- for (pass = 0 ; pass < 2 ; pass ++ ) {
219- /* pass 0 counts and allocates, pass 1 fills */
220- num_attr = 0 ;
221- cp = name + namelen ;
222- cp = cp + strspn (cp , blank );
223- while (* cp ) {
224- cp = parse_attr (src , lineno , cp , & num_attr , res );
225- if (!cp )
226- return NULL ;
227- }
228- if (pass )
229- break ;
230- res = xcalloc (1 ,
231- sizeof (* res ) +
232- sizeof (struct attr_state ) * num_attr +
233- (is_macro ? 0 : namelen + 1 ));
234- if (is_macro )
235- res -> u .attr = git_attr_internal (name , namelen );
236- else {
237- res -> u .pattern = (char * )& (res -> state [num_attr ]);
238- memcpy (res -> u .pattern , name , namelen );
239- res -> u .pattern [namelen ] = 0 ;
240- }
241- res -> is_macro = is_macro ;
242- res -> num_attr = num_attr ;
228+ states = name + namelen ;
229+ states += strspn (states , blank );
230+
231+ /* First pass to count the attr_states */
232+ for (cp = states , num_attr = 0 ; * cp ; num_attr ++ ) {
233+ cp = parse_attr (src , lineno , cp , NULL );
234+ if (!cp )
235+ return NULL ;
236+ }
237+
238+ res = xcalloc (1 ,
239+ sizeof (* res ) +
240+ sizeof (struct attr_state ) * num_attr +
241+ (is_macro ? 0 : namelen + 1 ));
242+ if (is_macro )
243+ res -> u .attr = git_attr_internal (name , namelen );
244+ else {
245+ res -> u .pattern = (char * )& (res -> state [num_attr ]);
246+ memcpy (res -> u .pattern , name , namelen );
247+ res -> u .pattern [namelen ] = 0 ;
243248 }
249+ res -> is_macro = is_macro ;
250+ res -> num_attr = num_attr ;
251+
252+ /* Second pass to fill the attr_states */
253+ for (cp = states , i = 0 ; * cp ; i ++ ) {
254+ cp = parse_attr (src , lineno , cp , & (res -> state [i ]));
255+ }
256+
244257 return res ;
245258}
246259
0 commit comments