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