Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 1 | #define NO_THE_INDEX_COMPATIBILITY_MACROS |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 2 | #include "cache.h" |
| 3 | #include "attr.h" |
| 4 | |
Junio C Hamano | a5e92ab | 2007-04-18 16:16:37 -0700 | [diff] [blame] | 5 | const char git_attr__true[] = "(builtin)true"; |
| 6 | const char git_attr__false[] = "\0(builtin)false"; |
| 7 | static const char git_attr__unknown[] = "(builtin)unknown"; |
| 8 | #define ATTR__TRUE git_attr__true |
| 9 | #define ATTR__FALSE git_attr__false |
| 10 | #define ATTR__UNSET NULL |
| 11 | #define ATTR__UNKNOWN git_attr__unknown |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 12 | |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 13 | /* |
| 14 | * The basic design decision here is that we are not going to have |
| 15 | * insanely large number of attributes. |
| 16 | * |
| 17 | * This is a randomly chosen prime. |
| 18 | */ |
| 19 | #define HASHSIZE 257 |
| 20 | |
| 21 | #ifndef DEBUG_ATTR |
| 22 | #define DEBUG_ATTR 0 |
| 23 | #endif |
| 24 | |
| 25 | struct git_attr { |
| 26 | struct git_attr *next; |
| 27 | unsigned h; |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 28 | int attr_nr; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 29 | char name[FLEX_ARRAY]; |
| 30 | }; |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 31 | static int attr_nr; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 32 | |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 33 | static struct git_attr_check *check_all_attr; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 34 | static struct git_attr *(git_attr_hash[HASHSIZE]); |
| 35 | |
| 36 | static unsigned hash_name(const char *name, int namelen) |
| 37 | { |
| 38 | unsigned val = 0; |
| 39 | unsigned char c; |
| 40 | |
| 41 | while (namelen--) { |
| 42 | c = *name++; |
| 43 | val = ((val << 7) | (val >> 22)) ^ c; |
| 44 | } |
| 45 | return val; |
| 46 | } |
| 47 | |
Junio C Hamano | e4aee10 | 2007-04-15 14:56:09 -0700 | [diff] [blame] | 48 | static int invalid_attr_name(const char *name, int namelen) |
| 49 | { |
| 50 | /* |
| 51 | * Attribute name cannot begin with '-' and from |
| 52 | * [-A-Za-z0-9_.]. We'd specifically exclude '=' for now, |
| 53 | * as we might later want to allow non-binary value for |
| 54 | * attributes, e.g. "*.svg merge=special-merge-program-for-svg" |
| 55 | */ |
| 56 | if (*name == '-') |
| 57 | return -1; |
| 58 | while (namelen--) { |
| 59 | char ch = *name++; |
| 60 | if (! (ch == '-' || ch == '.' || ch == '_' || |
| 61 | ('0' <= ch && ch <= '9') || |
| 62 | ('a' <= ch && ch <= 'z') || |
| 63 | ('A' <= ch && ch <= 'Z')) ) |
| 64 | return -1; |
| 65 | } |
| 66 | return 0; |
| 67 | } |
| 68 | |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 69 | struct git_attr *git_attr(const char *name, int len) |
| 70 | { |
| 71 | unsigned hval = hash_name(name, len); |
| 72 | unsigned pos = hval % HASHSIZE; |
| 73 | struct git_attr *a; |
| 74 | |
| 75 | for (a = git_attr_hash[pos]; a; a = a->next) { |
| 76 | if (a->h == hval && |
| 77 | !memcmp(a->name, name, len) && !a->name[len]) |
| 78 | return a; |
| 79 | } |
| 80 | |
Junio C Hamano | e4aee10 | 2007-04-15 14:56:09 -0700 | [diff] [blame] | 81 | if (invalid_attr_name(name, len)) |
| 82 | return NULL; |
| 83 | |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 84 | a = xmalloc(sizeof(*a) + len + 1); |
| 85 | memcpy(a->name, name, len); |
| 86 | a->name[len] = 0; |
| 87 | a->h = hval; |
| 88 | a->next = git_attr_hash[pos]; |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 89 | a->attr_nr = attr_nr++; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 90 | git_attr_hash[pos] = a; |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 91 | |
| 92 | check_all_attr = xrealloc(check_all_attr, |
| 93 | sizeof(*check_all_attr) * attr_nr); |
| 94 | check_all_attr[a->attr_nr].attr = a; |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 95 | check_all_attr[a->attr_nr].value = ATTR__UNKNOWN; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 96 | return a; |
| 97 | } |
| 98 | |
| 99 | /* |
| 100 | * .gitattributes file is one line per record, each of which is |
| 101 | * |
| 102 | * (1) glob pattern. |
| 103 | * (2) whitespace |
| 104 | * (3) whitespace separated list of attribute names, each of which |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 105 | * could be prefixed with '-' to mean "set to false", '!' to mean |
| 106 | * "unset". |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 107 | */ |
| 108 | |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 109 | /* What does a matched pattern decide? */ |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 110 | struct attr_state { |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 111 | struct git_attr *attr; |
Junio C Hamano | a5e92ab | 2007-04-18 16:16:37 -0700 | [diff] [blame] | 112 | const char *setto; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 113 | }; |
| 114 | |
| 115 | struct match_attr { |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 116 | union { |
| 117 | char *pattern; |
| 118 | struct git_attr *attr; |
| 119 | } u; |
| 120 | char is_macro; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 121 | unsigned num_attr; |
| 122 | struct attr_state state[FLEX_ARRAY]; |
| 123 | }; |
| 124 | |
| 125 | static const char blank[] = " \t\r\n"; |
| 126 | |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 127 | static const char *parse_attr(const char *src, int lineno, const char *cp, |
| 128 | int *num_attr, struct match_attr *res) |
| 129 | { |
| 130 | const char *ep, *equals; |
| 131 | int len; |
| 132 | |
| 133 | ep = cp + strcspn(cp, blank); |
| 134 | equals = strchr(cp, '='); |
| 135 | if (equals && ep < equals) |
| 136 | equals = NULL; |
| 137 | if (equals) |
| 138 | len = equals - cp; |
| 139 | else |
| 140 | len = ep - cp; |
| 141 | if (!res) { |
| 142 | if (*cp == '-' || *cp == '!') { |
| 143 | cp++; |
| 144 | len--; |
| 145 | } |
| 146 | if (invalid_attr_name(cp, len)) { |
| 147 | fprintf(stderr, |
| 148 | "%.*s is not a valid attribute name: %s:%d\n", |
| 149 | len, cp, src, lineno); |
| 150 | return NULL; |
| 151 | } |
| 152 | } else { |
| 153 | struct attr_state *e; |
| 154 | |
| 155 | e = &(res->state[*num_attr]); |
| 156 | if (*cp == '-' || *cp == '!') { |
| 157 | e->setto = (*cp == '-') ? ATTR__FALSE : ATTR__UNSET; |
| 158 | cp++; |
| 159 | len--; |
| 160 | } |
| 161 | else if (!equals) |
| 162 | e->setto = ATTR__TRUE; |
| 163 | else { |
Pierre Habouzit | 182af83 | 2007-09-16 00:32:36 +0200 | [diff] [blame] | 164 | e->setto = xmemdupz(equals + 1, ep - equals - 1); |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 165 | } |
| 166 | e->attr = git_attr(cp, len); |
| 167 | } |
| 168 | (*num_attr)++; |
| 169 | return ep + strspn(ep, blank); |
| 170 | } |
| 171 | |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 172 | static struct match_attr *parse_attr_line(const char *line, const char *src, |
| 173 | int lineno, int macro_ok) |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 174 | { |
| 175 | int namelen; |
| 176 | int num_attr; |
| 177 | const char *cp, *name; |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 178 | struct match_attr *res = NULL; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 179 | int pass; |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 180 | int is_macro; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 181 | |
| 182 | cp = line + strspn(line, blank); |
| 183 | if (!*cp || *cp == '#') |
| 184 | return NULL; |
| 185 | name = cp; |
| 186 | namelen = strcspn(name, blank); |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 187 | if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen && |
| 188 | !prefixcmp(name, ATTRIBUTE_MACRO_PREFIX)) { |
| 189 | if (!macro_ok) { |
| 190 | fprintf(stderr, "%s not allowed: %s:%d\n", |
| 191 | name, src, lineno); |
| 192 | return NULL; |
| 193 | } |
| 194 | is_macro = 1; |
| 195 | name += strlen(ATTRIBUTE_MACRO_PREFIX); |
| 196 | name += strspn(name, blank); |
| 197 | namelen = strcspn(name, blank); |
Junio C Hamano | e4aee10 | 2007-04-15 14:56:09 -0700 | [diff] [blame] | 198 | if (invalid_attr_name(name, namelen)) { |
| 199 | fprintf(stderr, |
| 200 | "%.*s is not a valid attribute name: %s:%d\n", |
| 201 | namelen, name, src, lineno); |
| 202 | return NULL; |
| 203 | } |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 204 | } |
| 205 | else |
| 206 | is_macro = 0; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 207 | |
| 208 | for (pass = 0; pass < 2; pass++) { |
| 209 | /* pass 0 counts and allocates, pass 1 fills */ |
| 210 | num_attr = 0; |
| 211 | cp = name + namelen; |
| 212 | cp = cp + strspn(cp, blank); |
Steffen Prohaska | d7b0a09 | 2007-10-18 22:02:35 +0200 | [diff] [blame] | 213 | while (*cp) { |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 214 | cp = parse_attr(src, lineno, cp, &num_attr, res); |
Steffen Prohaska | d7b0a09 | 2007-10-18 22:02:35 +0200 | [diff] [blame] | 215 | if (!cp) |
| 216 | return NULL; |
| 217 | } |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 218 | if (pass) |
| 219 | break; |
| 220 | res = xcalloc(1, |
| 221 | sizeof(*res) + |
| 222 | sizeof(struct attr_state) * num_attr + |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 223 | (is_macro ? 0 : namelen + 1)); |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 224 | if (is_macro) |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 225 | res->u.attr = git_attr(name, namelen); |
| 226 | else { |
| 227 | res->u.pattern = (char*)&(res->state[num_attr]); |
| 228 | memcpy(res->u.pattern, name, namelen); |
| 229 | res->u.pattern[namelen] = 0; |
| 230 | } |
| 231 | res->is_macro = is_macro; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 232 | res->num_attr = num_attr; |
| 233 | } |
| 234 | return res; |
| 235 | } |
| 236 | |
| 237 | /* |
| 238 | * Like info/exclude and .gitignore, the attribute information can |
| 239 | * come from many places. |
| 240 | * |
| 241 | * (1) .gitattribute file of the same directory; |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 242 | * (2) .gitattribute file of the parent directory if (1) does not have |
| 243 | * any match; this goes recursively upwards, just like .gitignore. |
| 244 | * (3) $GIT_DIR/info/attributes, which overrides both of the above. |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 245 | * |
| 246 | * In the same file, later entries override the earlier match, so in the |
| 247 | * global list, we would have entries from info/attributes the earliest |
| 248 | * (reading the file from top to bottom), .gitattribute of the root |
| 249 | * directory (again, reading the file from top to bottom) down to the |
| 250 | * current directory, and then scan the list backwards to find the first match. |
| 251 | * This is exactly the same as what excluded() does in dir.c to deal with |
| 252 | * .gitignore |
| 253 | */ |
| 254 | |
| 255 | static struct attr_stack { |
| 256 | struct attr_stack *prev; |
| 257 | char *origin; |
| 258 | unsigned num_matches; |
Junio C Hamano | a441311 | 2007-08-14 01:40:45 -0700 | [diff] [blame] | 259 | unsigned alloc; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 260 | struct match_attr **attrs; |
| 261 | } *attr_stack; |
| 262 | |
| 263 | static void free_attr_elem(struct attr_stack *e) |
| 264 | { |
| 265 | int i; |
| 266 | free(e->origin); |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 267 | for (i = 0; i < e->num_matches; i++) { |
| 268 | struct match_attr *a = e->attrs[i]; |
| 269 | int j; |
| 270 | for (j = 0; j < a->num_attr; j++) { |
Junio C Hamano | a5e92ab | 2007-04-18 16:16:37 -0700 | [diff] [blame] | 271 | const char *setto = a->state[j].setto; |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 272 | if (setto == ATTR__TRUE || |
| 273 | setto == ATTR__FALSE || |
| 274 | setto == ATTR__UNSET || |
| 275 | setto == ATTR__UNKNOWN) |
| 276 | ; |
| 277 | else |
Junio C Hamano | a5e92ab | 2007-04-18 16:16:37 -0700 | [diff] [blame] | 278 | free((char*) setto); |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 279 | } |
| 280 | free(a); |
| 281 | } |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 282 | free(e); |
| 283 | } |
| 284 | |
| 285 | static const char *builtin_attr[] = { |
Junio C Hamano | e4aee10 | 2007-04-15 14:56:09 -0700 | [diff] [blame] | 286 | "[attr]binary -diff -crlf", |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 287 | NULL, |
| 288 | }; |
| 289 | |
Junio C Hamano | a441311 | 2007-08-14 01:40:45 -0700 | [diff] [blame] | 290 | static void handle_attr_line(struct attr_stack *res, |
| 291 | const char *line, |
| 292 | const char *src, |
| 293 | int lineno, |
| 294 | int macro_ok) |
| 295 | { |
| 296 | struct match_attr *a; |
| 297 | |
| 298 | a = parse_attr_line(line, src, lineno, macro_ok); |
| 299 | if (!a) |
| 300 | return; |
| 301 | if (res->alloc <= res->num_matches) { |
| 302 | res->alloc = alloc_nr(res->num_matches); |
| 303 | res->attrs = xrealloc(res->attrs, |
| 304 | sizeof(struct match_attr *) * |
| 305 | res->alloc); |
| 306 | } |
| 307 | res->attrs[res->num_matches++] = a; |
| 308 | } |
| 309 | |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 310 | static struct attr_stack *read_attr_from_array(const char **list) |
| 311 | { |
| 312 | struct attr_stack *res; |
| 313 | const char *line; |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 314 | int lineno = 0; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 315 | |
| 316 | res = xcalloc(1, sizeof(*res)); |
Junio C Hamano | a441311 | 2007-08-14 01:40:45 -0700 | [diff] [blame] | 317 | while ((line = *(list++)) != NULL) |
| 318 | handle_attr_line(res, line, "[builtin]", ++lineno, 1); |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 319 | return res; |
| 320 | } |
| 321 | |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 322 | static enum git_attr_direction direction; |
| 323 | static struct index_state *use_index; |
| 324 | |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 325 | static struct attr_stack *read_attr_from_file(const char *path, int macro_ok) |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 326 | { |
Junio C Hamano | a441311 | 2007-08-14 01:40:45 -0700 | [diff] [blame] | 327 | FILE *fp = fopen(path, "r"); |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 328 | struct attr_stack *res; |
| 329 | char buf[2048]; |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 330 | int lineno = 0; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 331 | |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 332 | if (!fp) |
Junio C Hamano | a441311 | 2007-08-14 01:40:45 -0700 | [diff] [blame] | 333 | return NULL; |
| 334 | res = xcalloc(1, sizeof(*res)); |
| 335 | while (fgets(buf, sizeof(buf), fp)) |
| 336 | handle_attr_line(res, buf, path, ++lineno, macro_ok); |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 337 | fclose(fp); |
| 338 | return res; |
| 339 | } |
| 340 | |
Junio C Hamano | 1a9d7e9 | 2007-08-14 01:41:02 -0700 | [diff] [blame] | 341 | static void *read_index_data(const char *path) |
| 342 | { |
| 343 | int pos, len; |
| 344 | unsigned long sz; |
| 345 | enum object_type type; |
| 346 | void *data; |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 347 | struct index_state *istate = use_index ? use_index : &the_index; |
Junio C Hamano | 1a9d7e9 | 2007-08-14 01:41:02 -0700 | [diff] [blame] | 348 | |
| 349 | len = strlen(path); |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 350 | pos = index_name_pos(istate, path, len); |
Junio C Hamano | 1a9d7e9 | 2007-08-14 01:41:02 -0700 | [diff] [blame] | 351 | if (pos < 0) { |
| 352 | /* |
| 353 | * We might be in the middle of a merge, in which |
| 354 | * case we would read stage #2 (ours). |
| 355 | */ |
| 356 | int i; |
| 357 | for (i = -pos - 1; |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 358 | (pos < 0 && i < istate->cache_nr && |
| 359 | !strcmp(istate->cache[i]->name, path)); |
Junio C Hamano | 1a9d7e9 | 2007-08-14 01:41:02 -0700 | [diff] [blame] | 360 | i++) |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 361 | if (ce_stage(istate->cache[i]) == 2) |
Junio C Hamano | 1a9d7e9 | 2007-08-14 01:41:02 -0700 | [diff] [blame] | 362 | pos = i; |
| 363 | } |
| 364 | if (pos < 0) |
| 365 | return NULL; |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 366 | data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz); |
Junio C Hamano | 1a9d7e9 | 2007-08-14 01:41:02 -0700 | [diff] [blame] | 367 | if (!data || type != OBJ_BLOB) { |
| 368 | free(data); |
| 369 | return NULL; |
| 370 | } |
| 371 | return data; |
| 372 | } |
| 373 | |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 374 | static struct attr_stack *read_attr_from_index(const char *path, int macro_ok) |
Junio C Hamano | a441311 | 2007-08-14 01:40:45 -0700 | [diff] [blame] | 375 | { |
| 376 | struct attr_stack *res; |
Junio C Hamano | 1a9d7e9 | 2007-08-14 01:41:02 -0700 | [diff] [blame] | 377 | char *buf, *sp; |
| 378 | int lineno = 0; |
Junio C Hamano | a441311 | 2007-08-14 01:40:45 -0700 | [diff] [blame] | 379 | |
Junio C Hamano | 1a9d7e9 | 2007-08-14 01:41:02 -0700 | [diff] [blame] | 380 | buf = read_index_data(path); |
| 381 | if (!buf) |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 382 | return NULL; |
Junio C Hamano | 1a9d7e9 | 2007-08-14 01:41:02 -0700 | [diff] [blame] | 383 | |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 384 | res = xcalloc(1, sizeof(*res)); |
Junio C Hamano | 1a9d7e9 | 2007-08-14 01:41:02 -0700 | [diff] [blame] | 385 | for (sp = buf; *sp; ) { |
| 386 | char *ep; |
| 387 | int more; |
| 388 | for (ep = sp; *ep && *ep != '\n'; ep++) |
| 389 | ; |
| 390 | more = (*ep == '\n'); |
| 391 | *ep = '\0'; |
| 392 | handle_attr_line(res, sp, path, ++lineno, macro_ok); |
| 393 | sp = ep + more; |
| 394 | } |
| 395 | free(buf); |
Junio C Hamano | a441311 | 2007-08-14 01:40:45 -0700 | [diff] [blame] | 396 | return res; |
| 397 | } |
| 398 | |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 399 | static struct attr_stack *read_attr(const char *path, int macro_ok) |
| 400 | { |
| 401 | struct attr_stack *res; |
| 402 | |
| 403 | if (direction == GIT_ATTR_CHECKOUT) { |
| 404 | res = read_attr_from_index(path, macro_ok); |
| 405 | if (!res) |
| 406 | res = read_attr_from_file(path, macro_ok); |
| 407 | } |
| 408 | else { |
| 409 | res = read_attr_from_file(path, macro_ok); |
| 410 | if (!res) |
| 411 | /* |
| 412 | * There is no checked out .gitattributes file there, but |
| 413 | * we might have it in the index. We allow operation in a |
| 414 | * sparsely checked out work tree, so read from it. |
| 415 | */ |
| 416 | res = read_attr_from_index(path, macro_ok); |
| 417 | } |
| 418 | if (!res) |
| 419 | res = xcalloc(1, sizeof(*res)); |
| 420 | return res; |
| 421 | } |
| 422 | |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 423 | #if DEBUG_ATTR |
| 424 | static void debug_info(const char *what, struct attr_stack *elem) |
| 425 | { |
| 426 | fprintf(stderr, "%s: %s\n", what, elem->origin ? elem->origin : "()"); |
| 427 | } |
Junio C Hamano | cf94ccd | 2008-02-07 00:02:08 -0800 | [diff] [blame] | 428 | static void debug_set(const char *what, const char *match, struct git_attr *attr, const void *v) |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 429 | { |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 430 | const char *value = v; |
| 431 | |
| 432 | if (ATTR_TRUE(value)) |
| 433 | value = "set"; |
| 434 | else if (ATTR_FALSE(value)) |
| 435 | value = "unset"; |
| 436 | else if (ATTR_UNSET(value)) |
| 437 | value = "unspecified"; |
| 438 | |
| 439 | fprintf(stderr, "%s: %s => %s (%s)\n", |
| 440 | what, attr->name, (char *) value, match); |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 441 | } |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 442 | #define debug_push(a) debug_info("push", (a)) |
| 443 | #define debug_pop(a) debug_info("pop", (a)) |
| 444 | #else |
| 445 | #define debug_push(a) do { ; } while (0) |
| 446 | #define debug_pop(a) do { ; } while (0) |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 447 | #define debug_set(a,b,c,d) do { ; } while (0) |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 448 | #endif |
| 449 | |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 450 | static void drop_attr_stack(void) |
| 451 | { |
| 452 | while (attr_stack) { |
| 453 | struct attr_stack *elem = attr_stack; |
| 454 | attr_stack = elem->prev; |
| 455 | free_attr_elem(elem); |
| 456 | } |
| 457 | } |
| 458 | |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 459 | static void bootstrap_attr_stack(void) |
| 460 | { |
| 461 | if (!attr_stack) { |
| 462 | struct attr_stack *elem; |
| 463 | |
| 464 | elem = read_attr_from_array(builtin_attr); |
| 465 | elem->origin = NULL; |
| 466 | elem->prev = attr_stack; |
| 467 | attr_stack = elem; |
| 468 | |
René Scharfe | 2d35d55 | 2008-06-08 17:16:11 +0200 | [diff] [blame] | 469 | if (!is_bare_repository()) { |
| 470 | elem = read_attr(GITATTRIBUTES_FILE, 1); |
| 471 | elem->origin = strdup(""); |
| 472 | elem->prev = attr_stack; |
| 473 | attr_stack = elem; |
| 474 | debug_push(elem); |
| 475 | } |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 476 | |
| 477 | elem = read_attr_from_file(git_path(INFOATTRIBUTES_FILE), 1); |
Junio C Hamano | a441311 | 2007-08-14 01:40:45 -0700 | [diff] [blame] | 478 | if (!elem) |
| 479 | elem = xcalloc(1, sizeof(*elem)); |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 480 | elem->origin = NULL; |
| 481 | elem->prev = attr_stack; |
| 482 | attr_stack = elem; |
| 483 | } |
| 484 | } |
| 485 | |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 486 | static void prepare_attr_stack(const char *path, int dirlen) |
| 487 | { |
| 488 | struct attr_stack *elem, *info; |
| 489 | int len; |
Dmitry Potapov | f66cf96 | 2008-07-16 19:39:55 +0400 | [diff] [blame] | 490 | struct strbuf pathbuf; |
| 491 | |
| 492 | strbuf_init(&pathbuf, dirlen+2+strlen(GITATTRIBUTES_FILE)); |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 493 | |
| 494 | /* |
| 495 | * At the bottom of the attribute stack is the built-in |
| 496 | * set of attribute definitions. Then, contents from |
| 497 | * .gitattribute files from directories closer to the |
| 498 | * root to the ones in deeper directories are pushed |
| 499 | * to the stack. Finally, at the very top of the stack |
| 500 | * we always keep the contents of $GIT_DIR/info/attributes. |
| 501 | * |
| 502 | * When checking, we use entries from near the top of the |
| 503 | * stack, preferring $GIT_DIR/info/attributes, then |
| 504 | * .gitattributes in deeper directories to shallower ones, |
| 505 | * and finally use the built-in set as the default. |
| 506 | */ |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 507 | if (!attr_stack) |
| 508 | bootstrap_attr_stack(); |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 509 | |
| 510 | /* |
| 511 | * Pop the "info" one that is always at the top of the stack. |
| 512 | */ |
| 513 | info = attr_stack; |
| 514 | attr_stack = info->prev; |
| 515 | |
| 516 | /* |
| 517 | * Pop the ones from directories that are not the prefix of |
| 518 | * the path we are checking. |
| 519 | */ |
| 520 | while (attr_stack && attr_stack->origin) { |
| 521 | int namelen = strlen(attr_stack->origin); |
| 522 | |
| 523 | elem = attr_stack; |
| 524 | if (namelen <= dirlen && |
| 525 | !strncmp(elem->origin, path, namelen)) |
| 526 | break; |
| 527 | |
| 528 | debug_pop(elem); |
| 529 | attr_stack = elem->prev; |
| 530 | free_attr_elem(elem); |
| 531 | } |
| 532 | |
| 533 | /* |
| 534 | * Read from parent directories and push them down |
| 535 | */ |
René Scharfe | 2d35d55 | 2008-06-08 17:16:11 +0200 | [diff] [blame] | 536 | if (!is_bare_repository()) { |
| 537 | while (1) { |
| 538 | char *cp; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 539 | |
René Scharfe | 2d35d55 | 2008-06-08 17:16:11 +0200 | [diff] [blame] | 540 | len = strlen(attr_stack->origin); |
| 541 | if (dirlen <= len) |
| 542 | break; |
Dmitry Potapov | f66cf96 | 2008-07-16 19:39:55 +0400 | [diff] [blame] | 543 | strbuf_reset(&pathbuf); |
| 544 | strbuf_add(&pathbuf, path, dirlen); |
| 545 | strbuf_addch(&pathbuf, '/'); |
| 546 | cp = strchr(pathbuf.buf + len + 1, '/'); |
René Scharfe | 2d35d55 | 2008-06-08 17:16:11 +0200 | [diff] [blame] | 547 | strcpy(cp + 1, GITATTRIBUTES_FILE); |
Dmitry Potapov | f66cf96 | 2008-07-16 19:39:55 +0400 | [diff] [blame] | 548 | elem = read_attr(pathbuf.buf, 0); |
René Scharfe | 2d35d55 | 2008-06-08 17:16:11 +0200 | [diff] [blame] | 549 | *cp = '\0'; |
Dmitry Potapov | f66cf96 | 2008-07-16 19:39:55 +0400 | [diff] [blame] | 550 | elem->origin = strdup(pathbuf.buf); |
René Scharfe | 2d35d55 | 2008-06-08 17:16:11 +0200 | [diff] [blame] | 551 | elem->prev = attr_stack; |
| 552 | attr_stack = elem; |
| 553 | debug_push(elem); |
| 554 | } |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 555 | } |
| 556 | |
| 557 | /* |
| 558 | * Finally push the "info" one at the top of the stack. |
| 559 | */ |
| 560 | info->prev = attr_stack; |
| 561 | attr_stack = info; |
| 562 | } |
| 563 | |
| 564 | static int path_matches(const char *pathname, int pathlen, |
| 565 | const char *pattern, |
| 566 | const char *base, int baselen) |
| 567 | { |
| 568 | if (!strchr(pattern, '/')) { |
| 569 | /* match basename */ |
| 570 | const char *basename = strrchr(pathname, '/'); |
| 571 | basename = basename ? basename + 1 : pathname; |
| 572 | return (fnmatch(pattern, basename, 0) == 0); |
| 573 | } |
| 574 | /* |
| 575 | * match with FNM_PATHNAME; the pattern has base implicitly |
| 576 | * in front of it. |
| 577 | */ |
| 578 | if (*pattern == '/') |
| 579 | pattern++; |
| 580 | if (pathlen < baselen || |
Junio C Hamano | cf94ccd | 2008-02-07 00:02:08 -0800 | [diff] [blame] | 581 | (baselen && pathname[baselen] != '/') || |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 582 | strncmp(pathname, base, baselen)) |
| 583 | return 0; |
Matthew Ogilvie | 82881b3 | 2008-04-22 12:19:12 -0600 | [diff] [blame] | 584 | if (baselen != 0) |
| 585 | baselen++; |
| 586 | return fnmatch(pattern, pathname + baselen, FNM_PATHNAME) == 0; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 587 | } |
| 588 | |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 589 | static int fill_one(const char *what, struct match_attr *a, int rem) |
| 590 | { |
| 591 | struct git_attr_check *check = check_all_attr; |
| 592 | int i; |
| 593 | |
| 594 | for (i = 0; 0 < rem && i < a->num_attr; i++) { |
| 595 | struct git_attr *attr = a->state[i].attr; |
Junio C Hamano | a5e92ab | 2007-04-18 16:16:37 -0700 | [diff] [blame] | 596 | const char **n = &(check[attr->attr_nr].value); |
| 597 | const char *v = a->state[i].setto; |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 598 | |
| 599 | if (*n == ATTR__UNKNOWN) { |
| 600 | debug_set(what, a->u.pattern, attr, v); |
| 601 | *n = v; |
| 602 | rem--; |
| 603 | } |
| 604 | } |
| 605 | return rem; |
| 606 | } |
| 607 | |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 608 | static int fill(const char *path, int pathlen, struct attr_stack *stk, int rem) |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 609 | { |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 610 | int i; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 611 | const char *base = stk->origin ? stk->origin : ""; |
| 612 | |
| 613 | for (i = stk->num_matches - 1; 0 < rem && 0 <= i; i--) { |
| 614 | struct match_attr *a = stk->attrs[i]; |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 615 | if (a->is_macro) |
| 616 | continue; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 617 | if (path_matches(path, pathlen, |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 618 | a->u.pattern, base, strlen(base))) |
| 619 | rem = fill_one("fill", a, rem); |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 620 | } |
| 621 | return rem; |
| 622 | } |
| 623 | |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 624 | static int macroexpand(struct attr_stack *stk, int rem) |
| 625 | { |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 626 | int i; |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 627 | struct git_attr_check *check = check_all_attr; |
| 628 | |
| 629 | for (i = stk->num_matches - 1; 0 < rem && 0 <= i; i--) { |
| 630 | struct match_attr *a = stk->attrs[i]; |
| 631 | if (!a->is_macro) |
| 632 | continue; |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 633 | if (check[a->u.attr->attr_nr].value != ATTR__TRUE) |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 634 | continue; |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 635 | rem = fill_one("expand", a, rem); |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 636 | } |
| 637 | return rem; |
| 638 | } |
| 639 | |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 640 | int git_checkattr(const char *path, int num, struct git_attr_check *check) |
| 641 | { |
| 642 | struct attr_stack *stk; |
| 643 | const char *cp; |
| 644 | int dirlen, pathlen, i, rem; |
| 645 | |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 646 | bootstrap_attr_stack(); |
| 647 | for (i = 0; i < attr_nr; i++) |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 648 | check_all_attr[i].value = ATTR__UNKNOWN; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 649 | |
| 650 | pathlen = strlen(path); |
| 651 | cp = strrchr(path, '/'); |
| 652 | if (!cp) |
| 653 | dirlen = 0; |
| 654 | else |
| 655 | dirlen = cp - path; |
| 656 | prepare_attr_stack(path, dirlen); |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 657 | rem = attr_nr; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 658 | for (stk = attr_stack; 0 < rem && stk; stk = stk->prev) |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 659 | rem = fill(path, pathlen, stk, rem); |
| 660 | |
| 661 | for (stk = attr_stack; 0 < rem && stk; stk = stk->prev) |
| 662 | rem = macroexpand(stk, rem); |
| 663 | |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 664 | for (i = 0; i < num; i++) { |
Junio C Hamano | a5e92ab | 2007-04-18 16:16:37 -0700 | [diff] [blame] | 665 | const char *value = check_all_attr[check[i].attr->attr_nr].value; |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 666 | if (value == ATTR__UNKNOWN) |
| 667 | value = ATTR__UNSET; |
| 668 | check[i].value = value; |
| 669 | } |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 670 | |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 671 | return 0; |
| 672 | } |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 673 | |
| 674 | void git_attr_set_direction(enum git_attr_direction new, struct index_state *istate) |
| 675 | { |
| 676 | enum git_attr_direction old = direction; |
| 677 | direction = new; |
| 678 | if (new != old) |
| 679 | drop_attr_stack(); |
| 680 | use_index = istate; |
| 681 | } |